roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * @abstract
227  * @children Roo.bootstrap.Component
228  * Bootstrap Component base class
229  * @cfg {String} cls css class
230  * @cfg {String} style any extra css
231  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
233  * @cfg {string} dataId cutomer id
234  * @cfg {string} name Specifies name attribute
235  * @cfg {string} tooltip  Text for the tooltip
236  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
237  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
238  
239  * @constructor
240  * Do not use directly - it does not do anything..
241  * @param {Object} config The config object
242  */
243
244
245
246 Roo.bootstrap.Component = function(config){
247     Roo.bootstrap.Component.superclass.constructor.call(this, config);
248        
249     this.addEvents({
250         /**
251          * @event childrenrendered
252          * Fires when the children have been rendered..
253          * @param {Roo.bootstrap.Component} this
254          */
255         "childrenrendered" : true
256         
257         
258         
259     });
260     
261     
262 };
263
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
265     
266     
267     allowDomMove : false, // to stop relocations in parent onRender...
268     
269     cls : false,
270     
271     style : false,
272     
273     autoCreate : false,
274     
275     tooltip : null,
276     /**
277      * Initialize Events for the element
278      */
279     initEvents : function() { },
280     
281     xattr : false,
282     
283     parentId : false,
284     
285     can_build_overlaid : true,
286     
287     container_method : false,
288     
289     dataId : false,
290     
291     name : false,
292     
293     parent: function() {
294         // returns the parent component..
295         return Roo.ComponentMgr.get(this.parentId)
296         
297         
298     },
299     
300     // private
301     onRender : function(ct, position)
302     {
303        // Roo.log("Call onRender: " + this.xtype);
304         
305         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
306         
307         if(this.el){
308             if (this.el.attr('xtype')) {
309                 this.el.attr('xtypex', this.el.attr('xtype'));
310                 this.el.dom.removeAttribute('xtype');
311                 
312                 this.initEvents();
313             }
314             
315             return;
316         }
317         
318          
319         
320         var cfg = Roo.apply({},  this.getAutoCreate());
321         
322         cfg.id = this.id || Roo.id();
323         
324         // fill in the extra attributes 
325         if (this.xattr && typeof(this.xattr) =='object') {
326             for (var i in this.xattr) {
327                 cfg[i] = this.xattr[i];
328             }
329         }
330         
331         if(this.dataId){
332             cfg.dataId = this.dataId;
333         }
334         
335         if (this.cls) {
336             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
337         }
338         
339         if (this.style) { // fixme needs to support more complex style data.
340             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341         }
342         
343         if(this.name){
344             cfg.name = this.name;
345         }
346         
347         this.el = ct.createChild(cfg, position);
348         
349         if (this.tooltip) {
350             this.tooltipEl().attr('tooltip', this.tooltip);
351         }
352         
353         if(this.tabIndex !== undefined){
354             this.el.dom.setAttribute('tabIndex', this.tabIndex);
355         }
356         
357         this.initEvents();
358         
359     },
360     /**
361      * Fetch the element to add children to
362      * @return {Roo.Element} defaults to this.el
363      */
364     getChildContainer : function()
365     {
366         return this.el;
367     },
368     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
369     {
370         return Roo.get(document.body);
371     },
372     
373     /**
374      * Fetch the element to display the tooltip on.
375      * @return {Roo.Element} defaults to this.el
376      */
377     tooltipEl : function()
378     {
379         return this.el;
380     },
381         
382     addxtype  : function(tree,cntr)
383     {
384         var cn = this;
385         
386         cn = Roo.factory(tree);
387         //Roo.log(['addxtype', cn]);
388            
389         cn.parentType = this.xtype; //??
390         cn.parentId = this.id;
391         
392         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393         if (typeof(cn.container_method) == 'string') {
394             cntr = cn.container_method;
395         }
396         
397         
398         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
399         
400         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
401         
402         var build_from_html =  Roo.XComponent.build_from_html;
403           
404         var is_body  = (tree.xtype == 'Body') ;
405           
406         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
407           
408         var self_cntr_el = Roo.get(this[cntr](false));
409         
410         // do not try and build conditional elements 
411         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412             return false;
413         }
414         
415         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417                 return this.addxtypeChild(tree,cntr, is_body);
418             }
419             
420             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
421                 
422             if(echild){
423                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
424             }
425             
426             Roo.log('skipping render');
427             return cn;
428             
429         }
430         
431         var ret = false;
432         if (!build_from_html) {
433             return false;
434         }
435         
436         // this i think handles overlaying multiple children of the same type
437         // with the sam eelement.. - which might be buggy..
438         while (true) {
439             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
440             
441             if (!echild) {
442                 break;
443             }
444             
445             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446                 break;
447             }
448             
449             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
450         }
451        
452         return ret;
453     },
454     
455     
456     addxtypeChild : function (tree, cntr, is_body)
457     {
458         Roo.debug && Roo.log('addxtypeChild:' + cntr);
459         var cn = this;
460         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
461         
462         
463         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464                     (typeof(tree['flexy:foreach']) != 'undefined');
465           
466     
467         
468         skip_children = false;
469         // render the element if it's not BODY.
470         if (!is_body) {
471             
472             // if parent was disabled, then do not try and create the children..
473             if(!this[cntr](true)){
474                 tree.items = [];
475                 return tree;
476             }
477            
478             cn = Roo.factory(tree);
479            
480             cn.parentType = this.xtype; //??
481             cn.parentId = this.id;
482             
483             var build_from_html =  Roo.XComponent.build_from_html;
484             
485             
486             // does the container contain child eleemnts with 'xtype' attributes.
487             // that match this xtype..
488             // note - when we render we create these as well..
489             // so we should check to see if body has xtype set.
490             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
491                
492                 var self_cntr_el = Roo.get(this[cntr](false));
493                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
494                 if (echild) { 
495                     //Roo.log(Roo.XComponent.build_from_html);
496                     //Roo.log("got echild:");
497                     //Roo.log(echild);
498                 }
499                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500                 // and are not displayed -this causes this to use up the wrong element when matching.
501                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
502                 
503                 
504                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
506                   
507                   
508                   
509                     cn.el = echild;
510                   //  Roo.log("GOT");
511                     //echild.dom.removeAttribute('xtype');
512                 } else {
513                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514                     Roo.debug && Roo.log(self_cntr_el);
515                     Roo.debug && Roo.log(echild);
516                     Roo.debug && Roo.log(cn);
517                 }
518             }
519            
520             
521            
522             // if object has flexy:if - then it may or may not be rendered.
523             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
524                 // skip a flexy if element.
525                 Roo.debug && Roo.log('skipping render');
526                 Roo.debug && Roo.log(tree);
527                 if (!cn.el) {
528                     Roo.debug && Roo.log('skipping all children');
529                     skip_children = true;
530                 }
531                 
532              } else {
533                  
534                 // actually if flexy:foreach is found, we really want to create 
535                 // multiple copies here...
536                 //Roo.log('render');
537                 //Roo.log(this[cntr]());
538                 // some elements do not have render methods.. like the layouts...
539                 /*
540                 if(this[cntr](true) === false){
541                     cn.items = [];
542                     return cn;
543                 }
544                 */
545                 cn.render && cn.render(this[cntr](true));
546                 
547              }
548             // then add the element..
549         }
550          
551         // handle the kids..
552         
553         var nitems = [];
554         /*
555         if (typeof (tree.menu) != 'undefined') {
556             tree.menu.parentType = cn.xtype;
557             tree.menu.triggerEl = cn.el;
558             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559             
560         }
561         */
562         if (!tree.items || !tree.items.length) {
563             cn.items = nitems;
564             //Roo.log(["no children", this]);
565             
566             return cn;
567         }
568          
569         var items = tree.items;
570         delete tree.items;
571         
572         //Roo.log(items.length);
573             // add the items..
574         if (!skip_children) {    
575             for(var i =0;i < items.length;i++) {
576               //  Roo.log(['add child', items[i]]);
577                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
578             }
579         }
580         
581         cn.items = nitems;
582         
583         //Roo.log("fire childrenrendered");
584         
585         cn.fireEvent('childrenrendered', this);
586         
587         return cn;
588     },
589     
590     /**
591      * Set the element that will be used to show or hide
592      */
593     setVisibilityEl : function(el)
594     {
595         this.visibilityEl = el;
596     },
597     
598      /**
599      * Get the element that will be used to show or hide
600      */
601     getVisibilityEl : function()
602     {
603         if (typeof(this.visibilityEl) == 'object') {
604             return this.visibilityEl;
605         }
606         
607         if (typeof(this.visibilityEl) == 'string') {
608             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
609         }
610         
611         return this.getEl();
612     },
613     
614     /**
615      * Show a component - removes 'hidden' class
616      */
617     show : function()
618     {
619         if(!this.getVisibilityEl()){
620             return;
621         }
622          
623         this.getVisibilityEl().removeClass(['hidden','d-none']);
624         
625         this.fireEvent('show', this);
626         
627         
628     },
629     /**
630      * Hide a component - adds 'hidden' class
631      */
632     hide: function()
633     {
634         if(!this.getVisibilityEl()){
635             return;
636         }
637         
638         this.getVisibilityEl().addClass(['hidden','d-none']);
639         
640         this.fireEvent('hide', this);
641         
642     }
643 });
644
645  /*
646  * - LGPL
647  *
648  * element
649  * 
650  */
651
652 /**
653  * @class Roo.bootstrap.Element
654  * @extends Roo.bootstrap.Component
655  * @children Roo.bootstrap.Component
656  * Bootstrap Element class (basically a DIV used to make random stuff )
657  * 
658  * @cfg {String} html contents of the element
659  * @cfg {String} tag tag of the element
660  * @cfg {String} cls class of the element
661  * @cfg {Boolean} preventDefault (true|false) default false
662  * @cfg {Boolean} clickable (true|false) default false
663  * @cfg {String} role default blank - set to button to force cursor pointer
664  
665  * 
666  * @constructor
667  * Create a new Element
668  * @param {Object} config The config object
669  */
670
671 Roo.bootstrap.Element = function(config){
672     Roo.bootstrap.Element.superclass.constructor.call(this, config);
673     
674     this.addEvents({
675         // raw events
676         /**
677          * @event click
678          * When a element is chick
679          * @param {Roo.bootstrap.Element} this
680          * @param {Roo.EventObject} e
681          */
682         "click" : true 
683         
684       
685     });
686 };
687
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
689     
690     tag: 'div',
691     cls: '',
692     html: '',
693     preventDefault: false, 
694     clickable: false,
695     tapedTwice : false,
696     role : false,
697     
698     getAutoCreate : function(){
699         
700         var cfg = {
701             tag: this.tag,
702             // cls: this.cls, double assign in parent class Component.js :: onRender
703             html: this.html
704         };
705         if (this.role !== false) {
706             cfg.role = this.role;
707         }
708         
709         return cfg;
710     },
711     
712     initEvents: function() 
713     {
714         Roo.bootstrap.Element.superclass.initEvents.call(this);
715         
716         if(this.clickable){
717             this.el.on('click', this.onClick, this);
718         }
719         
720         
721     },
722     
723     onClick : function(e)
724     {
725         if(this.preventDefault){
726             e.preventDefault();
727         }
728         
729         this.fireEvent('click', this, e); // why was this double click before?
730     },
731     
732     
733     
734
735     
736     
737     getValue : function()
738     {
739         return this.el.dom.innerHTML;
740     },
741     
742     setValue : function(value)
743     {
744         this.el.dom.innerHTML = value;
745     }
746    
747 });
748
749  
750
751  /*
752  * - LGPL
753  *
754  * dropable area
755  * 
756  */
757
758 /**
759  * @class Roo.bootstrap.DropTarget
760  * @extends Roo.bootstrap.Element
761  * Bootstrap DropTarget class
762  
763  * @cfg {string} name dropable name
764  * 
765  * @constructor
766  * Create a new Dropable Area
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.DropTarget = function(config){
771     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772     
773     this.addEvents({
774         // raw events
775         /**
776          * @event click
777          * When a element is chick
778          * @param {Roo.bootstrap.Element} this
779          * @param {Roo.EventObject} e
780          */
781         "drop" : true
782     });
783 };
784
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
786     
787     
788     getAutoCreate : function(){
789         
790          
791     },
792     
793     initEvents: function() 
794     {
795         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
797             ddGroup: this.name,
798             listeners : {
799                 drop : this.dragDrop.createDelegate(this),
800                 enter : this.dragEnter.createDelegate(this),
801                 out : this.dragOut.createDelegate(this),
802                 over : this.dragOver.createDelegate(this)
803             }
804             
805         });
806         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
807     },
808     
809     dragDrop : function(source,e,data)
810     {
811         // user has to decide how to impliment this.
812         Roo.log('drop');
813         Roo.log(this);
814         //this.fireEvent('drop', this, source, e ,data);
815         return false;
816     },
817     
818     dragEnter : function(n, dd, e, data)
819     {
820         // probably want to resize the element to match the dropped element..
821         Roo.log("enter");
822         this.originalSize = this.el.getSize();
823         this.el.setSize( n.el.getSize());
824         this.dropZone.DDM.refreshCache(this.name);
825         Roo.log([n, dd, e, data]);
826     },
827     
828     dragOut : function(value)
829     {
830         // resize back to normal
831         Roo.log("out");
832         this.el.setSize(this.originalSize);
833         this.dropZone.resetConstraints();
834     },
835     
836     dragOver : function()
837     {
838         // ??? do nothing?
839     }
840    
841 });
842
843  
844
845  /*
846  * - LGPL
847  *
848  * Body
849  *
850  */
851
852 /**
853  * @class Roo.bootstrap.Body
854  * @extends Roo.bootstrap.Component
855  * @builder-top
856  * @children Roo.bootstrap.Component
857  * @parent none
858  * Bootstrap Body class
859  *
860  * @constructor
861  * Create a new body
862  * @param {Object} config The config object
863  */
864
865 Roo.bootstrap.Body = function(config){
866
867     config = config || {};
868
869     Roo.bootstrap.Body.superclass.constructor.call(this, config);
870     this.el = Roo.get(config.el ? config.el : document.body );
871     if (this.cls && this.cls.length) {
872         Roo.get(document.body).addClass(this.cls);
873     }
874 };
875
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
877
878     is_body : true,// just to make sure it's constructed?
879
880         autoCreate : {
881         cls: 'container'
882     },
883     onRender : function(ct, position)
884     {
885        /* Roo.log("Roo.bootstrap.Body - onRender");
886         if (this.cls && this.cls.length) {
887             Roo.get(document.body).addClass(this.cls);
888         }
889         // style??? xttr???
890         */
891     }
892
893
894
895
896 });
897 /*
898  * - LGPL
899  *
900  * button group
901  * 
902  */
903
904
905 /**
906  * @class Roo.bootstrap.ButtonGroup
907  * @extends Roo.bootstrap.Component
908  * Bootstrap ButtonGroup class
909  * @cfg {String} size lg | sm | xs (default empty normal)
910  * @cfg {String} align vertical | justified  (default none)
911  * @cfg {String} direction up | down (default down)
912  * @cfg {Boolean} toolbar false | true
913  * @cfg {Boolean} btn true | false
914  * 
915  * 
916  * @constructor
917  * Create a new Input
918  * @param {Object} config The config object
919  */
920
921 Roo.bootstrap.ButtonGroup = function(config){
922     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
923 };
924
925 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
926     
927     size: '',
928     align: '',
929     direction: '',
930     toolbar: false,
931     btn: true,
932
933     getAutoCreate : function(){
934         var cfg = {
935             cls: 'btn-group',
936             html : null
937         };
938         
939         cfg.html = this.html || cfg.html;
940         
941         if (this.toolbar) {
942             cfg = {
943                 cls: 'btn-toolbar',
944                 html: null
945             };
946             
947             return cfg;
948         }
949         
950         if (['vertical','justified'].indexOf(this.align)!==-1) {
951             cfg.cls = 'btn-group-' + this.align;
952             
953             if (this.align == 'justified') {
954                 console.log(this.items);
955             }
956         }
957         
958         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
959             cfg.cls += ' btn-group-' + this.size;
960         }
961         
962         if (this.direction == 'up') {
963             cfg.cls += ' dropup' ;
964         }
965         
966         return cfg;
967     },
968     /**
969      * Add a button to the group (similar to NavItem API.)
970      */
971     addItem : function(cfg)
972     {
973         var cn = new Roo.bootstrap.Button(cfg);
974         //this.register(cn);
975         cn.parentId = this.id;
976         cn.onRender(this.el, null);
977         return cn;
978     }
979    
980 });
981
982  /*
983  * - LGPL
984  *
985  * button
986  * 
987  */
988
989 /**
990  * @class Roo.bootstrap.Button
991  * @extends Roo.bootstrap.Component
992  * Bootstrap Button class
993  * @cfg {String} html The button content
994  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
995  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
996  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
997  * @cfg {String} size (lg|sm|xs)
998  * @cfg {String} tag (a|input|submit)
999  * @cfg {String} href empty or href
1000  * @cfg {Boolean} disabled default false;
1001  * @cfg {Boolean} isClose default false;
1002  * @cfg {String} glyphicon depricated - use fa
1003  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1004  * @cfg {String} badge text for badge
1005  * @cfg {String} theme (default|glow)  
1006  * @cfg {Boolean} inverse dark themed version
1007  * @cfg {Boolean} toggle is it a slidy toggle button
1008  * @cfg {Boolean} pressed   default null - if the button ahs active state
1009  * @cfg {String} ontext text for on slidy toggle state
1010  * @cfg {String} offtext text for off slidy toggle state
1011  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1012  * @cfg {Boolean} removeClass remove the standard class..
1013  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1014  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1015  * 
1016  * @constructor
1017  * Create a new button
1018  * @param {Object} config The config object
1019  */
1020
1021
1022 Roo.bootstrap.Button = function(config){
1023     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1024     
1025     this.addEvents({
1026         // raw events
1027         /**
1028          * @event click
1029          * When a button is pressed
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "click" : true,
1034         /**
1035          * @event dblclick
1036          * When a button is double clicked
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          */
1040         "dblclick" : true,
1041          /**
1042          * @event toggle
1043          * After the button has been toggles
1044          * @param {Roo.bootstrap.Button} btn
1045          * @param {Roo.EventObject} e
1046          * @param {boolean} pressed (also available as button.pressed)
1047          */
1048         "toggle" : true
1049     });
1050 };
1051
1052 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1053     html: false,
1054     active: false,
1055     weight: '',
1056     badge_weight: '',
1057     outline : false,
1058     size: '',
1059     tag: 'button',
1060     href: '',
1061     disabled: false,
1062     isClose: false,
1063     glyphicon: '',
1064     fa: '',
1065     badge: '',
1066     theme: 'default',
1067     inverse: false,
1068     
1069     toggle: false,
1070     ontext: 'ON',
1071     offtext: 'OFF',
1072     defaulton: true,
1073     preventDefault: true,
1074     removeClass: false,
1075     name: false,
1076     target: false,
1077     group : false,
1078      
1079     pressed : null,
1080      
1081     
1082     getAutoCreate : function(){
1083         
1084         var cfg = {
1085             tag : 'button',
1086             cls : 'roo-button',
1087             html: ''
1088         };
1089         
1090         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1091             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1092             this.tag = 'button';
1093         } else {
1094             cfg.tag = this.tag;
1095         }
1096         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1097         
1098         if (this.toggle == true) {
1099             cfg={
1100                 tag: 'div',
1101                 cls: 'slider-frame roo-button',
1102                 cn: [
1103                     {
1104                         tag: 'span',
1105                         'data-on-text':'ON',
1106                         'data-off-text':'OFF',
1107                         cls: 'slider-button',
1108                         html: this.offtext
1109                     }
1110                 ]
1111             };
1112             // why are we validating the weights?
1113             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1114                 cfg.cls +=  ' ' + this.weight;
1115             }
1116             
1117             return cfg;
1118         }
1119         
1120         if (this.isClose) {
1121             cfg.cls += ' close';
1122             
1123             cfg["aria-hidden"] = true;
1124             
1125             cfg.html = "&times;";
1126             
1127             return cfg;
1128         }
1129              
1130         
1131         if (this.theme==='default') {
1132             cfg.cls = 'btn roo-button';
1133             
1134             //if (this.parentType != 'Navbar') {
1135             this.weight = this.weight.length ?  this.weight : 'default';
1136             //}
1137             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1138                 
1139                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1140                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1141                 cfg.cls += ' btn-' + outline + weight;
1142                 if (this.weight == 'default') {
1143                     // BC
1144                     cfg.cls += ' btn-' + this.weight;
1145                 }
1146             }
1147         } else if (this.theme==='glow') {
1148             
1149             cfg.tag = 'a';
1150             cfg.cls = 'btn-glow roo-button';
1151             
1152             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1153                 
1154                 cfg.cls += ' ' + this.weight;
1155             }
1156         }
1157    
1158         
1159         if (this.inverse) {
1160             this.cls += ' inverse';
1161         }
1162         
1163         
1164         if (this.active || this.pressed === true) {
1165             cfg.cls += ' active';
1166         }
1167         
1168         if (this.disabled) {
1169             cfg.disabled = 'disabled';
1170         }
1171         
1172         if (this.items) {
1173             Roo.log('changing to ul' );
1174             cfg.tag = 'ul';
1175             this.glyphicon = 'caret';
1176             if (Roo.bootstrap.version == 4) {
1177                 this.fa = 'caret-down';
1178             }
1179             
1180         }
1181         
1182         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1183          
1184         //gsRoo.log(this.parentType);
1185         if (this.parentType === 'Navbar' && !this.parent().bar) {
1186             Roo.log('changing to li?');
1187             
1188             cfg.tag = 'li';
1189             
1190             cfg.cls = '';
1191             cfg.cn =  [{
1192                 tag : 'a',
1193                 cls : 'roo-button',
1194                 html : this.html,
1195                 href : this.href || '#'
1196             }];
1197             if (this.menu) {
1198                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1199                 cfg.cls += ' dropdown';
1200             }   
1201             
1202             delete cfg.html;
1203             
1204         }
1205         
1206        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1207         
1208         if (this.glyphicon) {
1209             cfg.html = ' ' + cfg.html;
1210             
1211             cfg.cn = [
1212                 {
1213                     tag: 'span',
1214                     cls: 'glyphicon glyphicon-' + this.glyphicon
1215                 }
1216             ];
1217         }
1218         if (this.fa) {
1219             cfg.html = ' ' + cfg.html;
1220             
1221             cfg.cn = [
1222                 {
1223                     tag: 'i',
1224                     cls: 'fa fas fa-' + this.fa
1225                 }
1226             ];
1227         }
1228         
1229         if (this.badge) {
1230             cfg.html += ' ';
1231             
1232             cfg.tag = 'a';
1233             
1234 //            cfg.cls='btn roo-button';
1235             
1236             cfg.href=this.href;
1237             
1238             var value = cfg.html;
1239             
1240             if(this.glyphicon){
1241                 value = {
1242                     tag: 'span',
1243                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1244                     html: this.html
1245                 };
1246             }
1247             if(this.fa){
1248                 value = {
1249                     tag: 'i',
1250                     cls: 'fa fas fa-' + this.fa,
1251                     html: this.html
1252                 };
1253             }
1254             
1255             var bw = this.badge_weight.length ? this.badge_weight :
1256                 (this.weight.length ? this.weight : 'secondary');
1257             bw = bw == 'default' ? 'secondary' : bw;
1258             
1259             cfg.cn = [
1260                 value,
1261                 {
1262                     tag: 'span',
1263                     cls: 'badge badge-' + bw,
1264                     html: this.badge
1265                 }
1266             ];
1267             
1268             cfg.html='';
1269         }
1270         
1271         if (this.menu) {
1272             cfg.cls += ' dropdown';
1273             cfg.html = typeof(cfg.html) != 'undefined' ?
1274                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1275         }
1276         
1277         if (cfg.tag !== 'a' && this.href !== '') {
1278             throw "Tag must be a to set href.";
1279         } else if (this.href.length > 0) {
1280             cfg.href = this.href;
1281         }
1282         
1283         if(this.removeClass){
1284             cfg.cls = '';
1285         }
1286         
1287         if(this.target){
1288             cfg.target = this.target;
1289         }
1290         
1291         return cfg;
1292     },
1293     initEvents: function() {
1294        // Roo.log('init events?');
1295 //        Roo.log(this.el.dom);
1296         // add the menu...
1297         
1298         if (typeof (this.menu) != 'undefined') {
1299             this.menu.parentType = this.xtype;
1300             this.menu.triggerEl = this.el;
1301             this.addxtype(Roo.apply({}, this.menu));
1302         }
1303
1304
1305         if (this.el.hasClass('roo-button')) {
1306              this.el.on('click', this.onClick, this);
1307              this.el.on('dblclick', this.onDblClick, this);
1308         } else {
1309              this.el.select('.roo-button').on('click', this.onClick, this);
1310              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1311              
1312         }
1313         // why?
1314         if(this.removeClass){
1315             this.el.on('click', this.onClick, this);
1316         }
1317         
1318         if (this.group === true) {
1319              if (this.pressed === false || this.pressed === true) {
1320                 // nothing
1321             } else {
1322                 this.pressed = false;
1323                 this.setActive(this.pressed);
1324             }
1325             
1326         }
1327         
1328         this.el.enableDisplayMode();
1329         
1330     },
1331     onClick : function(e)
1332     {
1333         if (this.disabled) {
1334             return;
1335         }
1336         
1337         Roo.log('button on click ');
1338         if(this.preventDefault){
1339             e.preventDefault();
1340         }
1341         
1342         if (this.group) {
1343             if (this.pressed) {
1344                 // do nothing -
1345                 return;
1346             }
1347             this.setActive(true);
1348             var pi = this.parent().items;
1349             for (var i = 0;i < pi.length;i++) {
1350                 if (this == pi[i]) {
1351                     continue;
1352                 }
1353                 if (pi[i].el.hasClass('roo-button')) {
1354                     pi[i].setActive(false);
1355                 }
1356             }
1357             this.fireEvent('click', this, e);            
1358             return;
1359         }
1360         
1361         if (this.pressed === true || this.pressed === false) {
1362             this.toggleActive(e);
1363         }
1364         
1365         
1366         this.fireEvent('click', this, e);
1367     },
1368     onDblClick: function(e)
1369     {
1370         if (this.disabled) {
1371             return;
1372         }
1373         if(this.preventDefault){
1374             e.preventDefault();
1375         }
1376         this.fireEvent('dblclick', this, e);
1377     },
1378     /**
1379      * Enables this button
1380      */
1381     enable : function()
1382     {
1383         this.disabled = false;
1384         this.el.removeClass('disabled');
1385         this.el.dom.removeAttribute("disabled");
1386     },
1387     
1388     /**
1389      * Disable this button
1390      */
1391     disable : function()
1392     {
1393         this.disabled = true;
1394         this.el.addClass('disabled');
1395         this.el.attr("disabled", "disabled")
1396     },
1397      /**
1398      * sets the active state on/off, 
1399      * @param {Boolean} state (optional) Force a particular state
1400      */
1401     setActive : function(v) {
1402         
1403         this.el[v ? 'addClass' : 'removeClass']('active');
1404         this.pressed = v;
1405     },
1406      /**
1407      * toggles the current active state 
1408      */
1409     toggleActive : function(e)
1410     {
1411         this.setActive(!this.pressed); // this modifies pressed...
1412         this.fireEvent('toggle', this, e, this.pressed);
1413     },
1414      /**
1415      * get the current active state
1416      * @return {boolean} true if it's active
1417      */
1418     isActive : function()
1419     {
1420         return this.el.hasClass('active');
1421     },
1422     /**
1423      * set the text of the first selected button
1424      */
1425     setText : function(str)
1426     {
1427         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1428     },
1429     /**
1430      * get the text of the first selected button
1431      */
1432     getText : function()
1433     {
1434         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1435     },
1436     
1437     setWeight : function(str)
1438     {
1439         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1440         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1441         this.weight = str;
1442         var outline = this.outline ? 'outline-' : '';
1443         if (str == 'default') {
1444             this.el.addClass('btn-default btn-outline-secondary');        
1445             return;
1446         }
1447         this.el.addClass('btn-' + outline + str);        
1448     }
1449     
1450     
1451 });
1452 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1453
1454 Roo.bootstrap.Button.weights = [
1455     'default',
1456     'secondary' ,
1457     'primary',
1458     'success',
1459     'info',
1460     'warning',
1461     'danger',
1462     'link',
1463     'light',
1464     'dark'              
1465    
1466 ];/*
1467  * - LGPL
1468  *
1469  * column
1470  * 
1471  */
1472
1473 /**
1474  * @class Roo.bootstrap.Column
1475  * @extends Roo.bootstrap.Component
1476  * @children Roo.bootstrap.Component
1477  * Bootstrap Column class
1478  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1479  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1480  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1481  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1482  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1483  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1484  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1485  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1486  *
1487  * 
1488  * @cfg {Boolean} hidden (true|false) hide the element
1489  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1490  * @cfg {String} fa (ban|check|...) font awesome icon
1491  * @cfg {Number} fasize (1|2|....) font awsome size
1492
1493  * @cfg {String} icon (info-sign|check|...) glyphicon name
1494
1495  * @cfg {String} html content of column.
1496  * 
1497  * @constructor
1498  * Create a new Column
1499  * @param {Object} config The config object
1500  */
1501
1502 Roo.bootstrap.Column = function(config){
1503     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1504 };
1505
1506 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1507     
1508     xs: false,
1509     sm: false,
1510     md: false,
1511     lg: false,
1512     xsoff: false,
1513     smoff: false,
1514     mdoff: false,
1515     lgoff: false,
1516     html: '',
1517     offset: 0,
1518     alert: false,
1519     fa: false,
1520     icon : false,
1521     hidden : false,
1522     fasize : 1,
1523     
1524     getAutoCreate : function(){
1525         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1526         
1527         cfg = {
1528             tag: 'div',
1529             cls: 'column'
1530         };
1531         
1532         var settings=this;
1533         var sizes =   ['xs','sm','md','lg'];
1534         sizes.map(function(size ,ix){
1535             //Roo.log( size + ':' + settings[size]);
1536             
1537             if (settings[size+'off'] !== false) {
1538                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1539             }
1540             
1541             if (settings[size] === false) {
1542                 return;
1543             }
1544             
1545             if (!settings[size]) { // 0 = hidden
1546                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1547                 // bootsrap4
1548                 for (var i = ix; i > -1; i--) {
1549                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1550                 }
1551                 
1552                 
1553                 return;
1554             }
1555             cfg.cls += ' col-' + size + '-' + settings[size] + (
1556                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1557             );
1558             
1559         });
1560         
1561         if (this.hidden) {
1562             cfg.cls += ' hidden';
1563         }
1564         
1565         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1566             cfg.cls +=' alert alert-' + this.alert;
1567         }
1568         
1569         
1570         if (this.html.length) {
1571             cfg.html = this.html;
1572         }
1573         if (this.fa) {
1574             var fasize = '';
1575             if (this.fasize > 1) {
1576                 fasize = ' fa-' + this.fasize + 'x';
1577             }
1578             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1579             
1580             
1581         }
1582         if (this.icon) {
1583             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1584         }
1585         
1586         return cfg;
1587     }
1588    
1589 });
1590
1591  
1592
1593  /*
1594  * - LGPL
1595  *
1596  * page container.
1597  * 
1598  */
1599
1600
1601 /**
1602  * @class Roo.bootstrap.Container
1603  * @extends Roo.bootstrap.Component
1604  * @builder-top
1605  * @children Roo.bootstrap.Component
1606  * Bootstrap Container class
1607  * @cfg {Boolean} jumbotron is it a jumbotron element
1608  * @cfg {String} html content of element
1609  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1610  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1611  * @cfg {String} header content of header (for panel)
1612  * @cfg {String} footer content of footer (for panel)
1613  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1614  * @cfg {String} tag (header|aside|section) type of HTML tag.
1615  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1616  * @cfg {String} fa font awesome icon
1617  * @cfg {String} icon (info-sign|check|...) glyphicon name
1618  * @cfg {Boolean} hidden (true|false) hide the element
1619  * @cfg {Boolean} expandable (true|false) default false
1620  * @cfg {Boolean} expanded (true|false) default true
1621  * @cfg {String} rheader contet on the right of header
1622  * @cfg {Boolean} clickable (true|false) default false
1623
1624  *     
1625  * @constructor
1626  * Create a new Container
1627  * @param {Object} config The config object
1628  */
1629
1630 Roo.bootstrap.Container = function(config){
1631     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1632     
1633     this.addEvents({
1634         // raw events
1635          /**
1636          * @event expand
1637          * After the panel has been expand
1638          * 
1639          * @param {Roo.bootstrap.Container} this
1640          */
1641         "expand" : true,
1642         /**
1643          * @event collapse
1644          * After the panel has been collapsed
1645          * 
1646          * @param {Roo.bootstrap.Container} this
1647          */
1648         "collapse" : true,
1649         /**
1650          * @event click
1651          * When a element is chick
1652          * @param {Roo.bootstrap.Container} this
1653          * @param {Roo.EventObject} e
1654          */
1655         "click" : true
1656     });
1657 };
1658
1659 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1660     
1661     jumbotron : false,
1662     well: '',
1663     panel : '',
1664     header: '',
1665     footer : '',
1666     sticky: '',
1667     tag : false,
1668     alert : false,
1669     fa: false,
1670     icon : false,
1671     expandable : false,
1672     rheader : '',
1673     expanded : true,
1674     clickable: false,
1675   
1676      
1677     getChildContainer : function() {
1678         
1679         if(!this.el){
1680             return false;
1681         }
1682         
1683         if (this.panel.length) {
1684             return this.el.select('.panel-body',true).first();
1685         }
1686         
1687         return this.el;
1688     },
1689     
1690     
1691     getAutoCreate : function(){
1692         
1693         var cfg = {
1694             tag : this.tag || 'div',
1695             html : '',
1696             cls : ''
1697         };
1698         if (this.jumbotron) {
1699             cfg.cls = 'jumbotron';
1700         }
1701         
1702         
1703         
1704         // - this is applied by the parent..
1705         //if (this.cls) {
1706         //    cfg.cls = this.cls + '';
1707         //}
1708         
1709         if (this.sticky.length) {
1710             
1711             var bd = Roo.get(document.body);
1712             if (!bd.hasClass('bootstrap-sticky')) {
1713                 bd.addClass('bootstrap-sticky');
1714                 Roo.select('html',true).setStyle('height', '100%');
1715             }
1716              
1717             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1718         }
1719         
1720         
1721         if (this.well.length) {
1722             switch (this.well) {
1723                 case 'lg':
1724                 case 'sm':
1725                     cfg.cls +=' well well-' +this.well;
1726                     break;
1727                 default:
1728                     cfg.cls +=' well';
1729                     break;
1730             }
1731         }
1732         
1733         if (this.hidden) {
1734             cfg.cls += ' hidden';
1735         }
1736         
1737         
1738         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1739             cfg.cls +=' alert alert-' + this.alert;
1740         }
1741         
1742         var body = cfg;
1743         
1744         if (this.panel.length) {
1745             cfg.cls += ' panel panel-' + this.panel;
1746             cfg.cn = [];
1747             if (this.header.length) {
1748                 
1749                 var h = [];
1750                 
1751                 if(this.expandable){
1752                     
1753                     cfg.cls = cfg.cls + ' expandable';
1754                     
1755                     h.push({
1756                         tag: 'i',
1757                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1758                     });
1759                     
1760                 }
1761                 
1762                 h.push(
1763                     {
1764                         tag: 'span',
1765                         cls : 'panel-title',
1766                         html : (this.expandable ? '&nbsp;' : '') + this.header
1767                     },
1768                     {
1769                         tag: 'span',
1770                         cls: 'panel-header-right',
1771                         html: this.rheader
1772                     }
1773                 );
1774                 
1775                 cfg.cn.push({
1776                     cls : 'panel-heading',
1777                     style : this.expandable ? 'cursor: pointer' : '',
1778                     cn : h
1779                 });
1780                 
1781             }
1782             
1783             body = false;
1784             cfg.cn.push({
1785                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1786                 html : this.html
1787             });
1788             
1789             
1790             if (this.footer.length) {
1791                 cfg.cn.push({
1792                     cls : 'panel-footer',
1793                     html : this.footer
1794                     
1795                 });
1796             }
1797             
1798         }
1799         
1800         if (body) {
1801             body.html = this.html || cfg.html;
1802             // prefix with the icons..
1803             if (this.fa) {
1804                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1805             }
1806             if (this.icon) {
1807                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1808             }
1809             
1810             
1811         }
1812         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1813             cfg.cls =  'container';
1814         }
1815         
1816         return cfg;
1817     },
1818     
1819     initEvents: function() 
1820     {
1821         if(this.expandable){
1822             var headerEl = this.headerEl();
1823         
1824             if(headerEl){
1825                 headerEl.on('click', this.onToggleClick, this);
1826             }
1827         }
1828         
1829         if(this.clickable){
1830             this.el.on('click', this.onClick, this);
1831         }
1832         
1833     },
1834     
1835     onToggleClick : function()
1836     {
1837         var headerEl = this.headerEl();
1838         
1839         if(!headerEl){
1840             return;
1841         }
1842         
1843         if(this.expanded){
1844             this.collapse();
1845             return;
1846         }
1847         
1848         this.expand();
1849     },
1850     
1851     expand : function()
1852     {
1853         if(this.fireEvent('expand', this)) {
1854             
1855             this.expanded = true;
1856             
1857             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1858             
1859             this.el.select('.panel-body',true).first().removeClass('hide');
1860             
1861             var toggleEl = this.toggleEl();
1862
1863             if(!toggleEl){
1864                 return;
1865             }
1866
1867             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1868         }
1869         
1870     },
1871     
1872     collapse : function()
1873     {
1874         if(this.fireEvent('collapse', this)) {
1875             
1876             this.expanded = false;
1877             
1878             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1879             this.el.select('.panel-body',true).first().addClass('hide');
1880         
1881             var toggleEl = this.toggleEl();
1882
1883             if(!toggleEl){
1884                 return;
1885             }
1886
1887             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1888         }
1889     },
1890     
1891     toggleEl : function()
1892     {
1893         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1894             return;
1895         }
1896         
1897         return this.el.select('.panel-heading .fa',true).first();
1898     },
1899     
1900     headerEl : function()
1901     {
1902         if(!this.el || !this.panel.length || !this.header.length){
1903             return;
1904         }
1905         
1906         return this.el.select('.panel-heading',true).first()
1907     },
1908     
1909     bodyEl : function()
1910     {
1911         if(!this.el || !this.panel.length){
1912             return;
1913         }
1914         
1915         return this.el.select('.panel-body',true).first()
1916     },
1917     
1918     titleEl : function()
1919     {
1920         if(!this.el || !this.panel.length || !this.header.length){
1921             return;
1922         }
1923         
1924         return this.el.select('.panel-title',true).first();
1925     },
1926     
1927     setTitle : function(v)
1928     {
1929         var titleEl = this.titleEl();
1930         
1931         if(!titleEl){
1932             return;
1933         }
1934         
1935         titleEl.dom.innerHTML = v;
1936     },
1937     
1938     getTitle : function()
1939     {
1940         
1941         var titleEl = this.titleEl();
1942         
1943         if(!titleEl){
1944             return '';
1945         }
1946         
1947         return titleEl.dom.innerHTML;
1948     },
1949     
1950     setRightTitle : function(v)
1951     {
1952         var t = this.el.select('.panel-header-right',true).first();
1953         
1954         if(!t){
1955             return;
1956         }
1957         
1958         t.dom.innerHTML = v;
1959     },
1960     
1961     onClick : function(e)
1962     {
1963         e.preventDefault();
1964         
1965         this.fireEvent('click', this, e);
1966     }
1967 });
1968
1969  /*
1970  *  - LGPL
1971  *
1972  *  This is BS4's Card element.. - similar to our containers probably..
1973  * 
1974  */
1975 /**
1976  * @class Roo.bootstrap.Card
1977  * @extends Roo.bootstrap.Component
1978  * @children Roo.bootstrap.Component
1979  * Bootstrap Card class
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * Bootstrap CardHeader class
2781  * @constructor
2782  * Create a new Card Header - that you can embed children into
2783  * @param {Object} config The config object
2784  */
2785
2786 Roo.bootstrap.CardHeader = function(config){
2787     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2788 };
2789
2790 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2791     
2792     
2793     container_method : 'getCardHeader' 
2794     
2795      
2796     
2797     
2798    
2799 });
2800
2801  
2802
2803  /*
2804  * - LGPL
2805  *
2806  * Card footer - holder for the card footer elements.
2807  * 
2808  */
2809
2810 /**
2811  * @class Roo.bootstrap.CardFooter
2812  * @extends Roo.bootstrap.Element
2813  * Bootstrap CardFooter class
2814  * @constructor
2815  * Create a new Card Footer - that you can embed children into
2816  * @param {Object} config The config object
2817  */
2818
2819 Roo.bootstrap.CardFooter = function(config){
2820     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2821 };
2822
2823 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2824     
2825     
2826     container_method : 'getCardFooter' 
2827     
2828      
2829     
2830     
2831    
2832 });
2833
2834  
2835
2836  /*
2837  * - LGPL
2838  *
2839  * Card header - holder for the card header elements.
2840  * 
2841  */
2842
2843 /**
2844  * @class Roo.bootstrap.CardImageTop
2845  * @extends Roo.bootstrap.Element
2846  * Bootstrap CardImageTop class
2847  * @constructor
2848  * Create a new Card Image Top container
2849  * @param {Object} config The config object
2850  */
2851
2852 Roo.bootstrap.CardImageTop = function(config){
2853     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2854 };
2855
2856 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2857     
2858    
2859     container_method : 'getCardImageTop' 
2860     
2861      
2862     
2863    
2864 });
2865
2866  
2867
2868  
2869 /*
2870 * Licence: LGPL
2871 */
2872
2873 /**
2874  * @class Roo.bootstrap.ButtonUploader
2875  * @extends Roo.bootstrap.Button
2876  * Bootstrap Button Uploader class - it's a button which when you add files to it
2877  *
2878  * 
2879  * @cfg {Number} errorTimeout default 3000
2880  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2881  * @cfg {Array}  html The button text.
2882  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2883  *
2884  * @constructor
2885  * Create a new CardUploader
2886  * @param {Object} config The config object
2887  */
2888
2889 Roo.bootstrap.ButtonUploader = function(config){
2890     
2891  
2892     
2893     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2894     
2895      
2896      this.addEvents({
2897          // raw events
2898         /**
2899          * @event beforeselect
2900          * When button is pressed, before show upload files dialog is shown
2901          * @param {Roo.bootstrap.UploaderButton} this
2902          *
2903          */
2904         'beforeselect' : true,
2905          /**
2906          * @event fired when files have been selected, 
2907          * When a the download link is clicked
2908          * @param {Roo.bootstrap.UploaderButton} this
2909          * @param {Array} Array of files that have been uploaded
2910          */
2911         'uploaded' : true
2912         
2913     });
2914 };
2915  
2916 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2917     
2918      
2919     errorTimeout : 3000,
2920      
2921     images : false,
2922    
2923     fileCollection : false,
2924     allowBlank : true,
2925     
2926     multiple : true,
2927     
2928     getAutoCreate : function()
2929     {
2930         var im = {
2931             tag: 'input',
2932             type : 'file',
2933             cls : 'd-none  roo-card-upload-selector' 
2934           
2935         };
2936         if (this.multiple) {
2937             im.multiple = 'multiple';
2938         }
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2944                 im
2945
2946             ]
2947         };
2948            
2949          
2950     },
2951      
2952    
2953     initEvents : function()
2954     {
2955         
2956         Roo.bootstrap.Button.prototype.initEvents.call(this);
2957         
2958         
2959         
2960         
2961         
2962         this.urlAPI = (window.createObjectURL && window) || 
2963                                 (window.URL && URL.revokeObjectURL && URL) || 
2964                                 (window.webkitURL && webkitURL);
2965                         
2966          
2967          
2968          
2969         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2970         
2971         this.selectorEl.on('change', this.onFileSelected, this);
2972          
2973          
2974        
2975     },
2976     
2977    
2978     onClick : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if ( this.fireEvent('beforeselect', this) === false) {
2983             return;
2984         }
2985          
2986         this.selectorEl.dom.click();
2987          
2988     },
2989     
2990     onFileSelected : function(e)
2991     {
2992         e.preventDefault();
2993         
2994         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2995             return;
2996         }
2997         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2998         this.selectorEl.dom.value  = '';// hopefully reset..
2999         
3000         this.fireEvent('uploaded', this,  files );
3001         
3002     },
3003     
3004        
3005    
3006     
3007     /**
3008      * addCard - add an Attachment to the uploader
3009      * @param data - the data about the image to upload
3010      *
3011      * {
3012           id : 123
3013           title : "Title of file",
3014           is_uploaded : false,
3015           src : "http://.....",
3016           srcfile : { the File upload object },
3017           mimetype : file.type,
3018           preview : false,
3019           is_deleted : 0
3020           .. any other data...
3021         }
3022      *
3023      * 
3024     */
3025      
3026     reset: function()
3027     {
3028          
3029          this.selectorEl
3030     } 
3031     
3032     
3033     
3034     
3035 });
3036  /*
3037  * - LGPL
3038  *
3039  * image
3040  * 
3041  */
3042
3043
3044 /**
3045  * @class Roo.bootstrap.Img
3046  * @extends Roo.bootstrap.Component
3047  * Bootstrap Img class
3048  * @cfg {Boolean} imgResponsive false | true
3049  * @cfg {String} border rounded | circle | thumbnail
3050  * @cfg {String} src image source
3051  * @cfg {String} alt image alternative text
3052  * @cfg {String} href a tag href
3053  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3054  * @cfg {String} xsUrl xs image source
3055  * @cfg {String} smUrl sm image source
3056  * @cfg {String} mdUrl md image source
3057  * @cfg {String} lgUrl lg image source
3058  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3059  * 
3060  * @constructor
3061  * Create a new Input
3062  * @param {Object} config The config object
3063  */
3064
3065 Roo.bootstrap.Img = function(config){
3066     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3067     
3068     this.addEvents({
3069         // img events
3070         /**
3071          * @event click
3072          * The img click event for the img.
3073          * @param {Roo.EventObject} e
3074          */
3075         "click" : true,
3076         /**
3077          * @event load
3078          * The when any image loads
3079          * @param {Roo.EventObject} e
3080          */
3081         "load" : true
3082     });
3083 };
3084
3085 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3086     
3087     imgResponsive: true,
3088     border: '',
3089     src: 'about:blank',
3090     href: false,
3091     target: false,
3092     xsUrl: '',
3093     smUrl: '',
3094     mdUrl: '',
3095     lgUrl: '',
3096     backgroundContain : false,
3097
3098     getAutoCreate : function()
3099     {   
3100         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3101             return this.createSingleImg();
3102         }
3103         
3104         var cfg = {
3105             tag: 'div',
3106             cls: 'roo-image-responsive-group',
3107             cn: []
3108         };
3109         var _this = this;
3110         
3111         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3112             
3113             if(!_this[size + 'Url']){
3114                 return;
3115             }
3116             
3117             var img = {
3118                 tag: 'img',
3119                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3120                 html: _this.html || cfg.html,
3121                 src: _this[size + 'Url']
3122             };
3123             
3124             img.cls += ' roo-image-responsive-' + size;
3125             
3126             var s = ['xs', 'sm', 'md', 'lg'];
3127             
3128             s.splice(s.indexOf(size), 1);
3129             
3130             Roo.each(s, function(ss){
3131                 img.cls += ' hidden-' + ss;
3132             });
3133             
3134             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3135                 cfg.cls += ' img-' + _this.border;
3136             }
3137             
3138             if(_this.alt){
3139                 cfg.alt = _this.alt;
3140             }
3141             
3142             if(_this.href){
3143                 var a = {
3144                     tag: 'a',
3145                     href: _this.href,
3146                     cn: [
3147                         img
3148                     ]
3149                 };
3150
3151                 if(this.target){
3152                     a.target = _this.target;
3153                 }
3154             }
3155             
3156             cfg.cn.push((_this.href) ? a : img);
3157             
3158         });
3159         
3160         return cfg;
3161     },
3162     
3163     createSingleImg : function()
3164     {
3165         var cfg = {
3166             tag: 'img',
3167             cls: (this.imgResponsive) ? 'img-responsive' : '',
3168             html : null,
3169             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3170         };
3171         
3172         if (this.backgroundContain) {
3173             cfg.cls += ' background-contain';
3174         }
3175         
3176         cfg.html = this.html || cfg.html;
3177         
3178         if (this.backgroundContain) {
3179             cfg.style="background-image: url(" + this.src + ')';
3180         } else {
3181             cfg.src = this.src || cfg.src;
3182         }
3183         
3184         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3185             cfg.cls += ' img-' + this.border;
3186         }
3187         
3188         if(this.alt){
3189             cfg.alt = this.alt;
3190         }
3191         
3192         if(this.href){
3193             var a = {
3194                 tag: 'a',
3195                 href: this.href,
3196                 cn: [
3197                     cfg
3198                 ]
3199             };
3200             
3201             if(this.target){
3202                 a.target = this.target;
3203             }
3204             
3205         }
3206         
3207         return (this.href) ? a : cfg;
3208     },
3209     
3210     initEvents: function() 
3211     {
3212         if(!this.href){
3213             this.el.on('click', this.onClick, this);
3214         }
3215         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3216             this.el.on('load', this.onImageLoad, this);
3217         } else {
3218             // not sure if this works.. not tested
3219             this.el.select('img', true).on('load', this.onImageLoad, this);
3220         }
3221         
3222     },
3223     
3224     onClick : function(e)
3225     {
3226         Roo.log('img onclick');
3227         this.fireEvent('click', this, e);
3228     },
3229     onImageLoad: function(e)
3230     {
3231         Roo.log('img load');
3232         this.fireEvent('load', this, e);
3233     },
3234     
3235     /**
3236      * Sets the url of the image - used to update it
3237      * @param {String} url the url of the image
3238      */
3239     
3240     setSrc : function(url)
3241     {
3242         this.src =  url;
3243         
3244         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3245             if (this.backgroundContain) {
3246                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3247             } else {
3248                 this.el.dom.src =  url;
3249             }
3250             return;
3251         }
3252         
3253         this.el.select('img', true).first().dom.src =  url;
3254     }
3255     
3256     
3257    
3258 });
3259
3260  /*
3261  * - LGPL
3262  *
3263  * image
3264  * 
3265  */
3266
3267
3268 /**
3269  * @class Roo.bootstrap.Link
3270  * @extends Roo.bootstrap.Component
3271  * Bootstrap Link Class
3272  * @cfg {String} alt image alternative text
3273  * @cfg {String} href a tag href
3274  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3275  * @cfg {String} html the content of the link.
3276  * @cfg {String} anchor name for the anchor link
3277  * @cfg {String} fa - favicon
3278
3279  * @cfg {Boolean} preventDefault (true | false) default false
3280
3281  * 
3282  * @constructor
3283  * Create a new Input
3284  * @param {Object} config The config object
3285  */
3286
3287 Roo.bootstrap.Link = function(config){
3288     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3289     
3290     this.addEvents({
3291         // img events
3292         /**
3293          * @event click
3294          * The img click event for the img.
3295          * @param {Roo.EventObject} e
3296          */
3297         "click" : true
3298     });
3299 };
3300
3301 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3302     
3303     href: false,
3304     target: false,
3305     preventDefault: false,
3306     anchor : false,
3307     alt : false,
3308     fa: false,
3309
3310
3311     getAutoCreate : function()
3312     {
3313         var html = this.html || '';
3314         
3315         if (this.fa !== false) {
3316             html = '<i class="fa fa-' + this.fa + '"></i>';
3317         }
3318         var cfg = {
3319             tag: 'a'
3320         };
3321         // anchor's do not require html/href...
3322         if (this.anchor === false) {
3323             cfg.html = html;
3324             cfg.href = this.href || '#';
3325         } else {
3326             cfg.name = this.anchor;
3327             if (this.html !== false || this.fa !== false) {
3328                 cfg.html = html;
3329             }
3330             if (this.href !== false) {
3331                 cfg.href = this.href;
3332             }
3333         }
3334         
3335         if(this.alt !== false){
3336             cfg.alt = this.alt;
3337         }
3338         
3339         
3340         if(this.target !== false) {
3341             cfg.target = this.target;
3342         }
3343         
3344         return cfg;
3345     },
3346     
3347     initEvents: function() {
3348         
3349         if(!this.href || this.preventDefault){
3350             this.el.on('click', this.onClick, this);
3351         }
3352     },
3353     
3354     onClick : function(e)
3355     {
3356         if(this.preventDefault){
3357             e.preventDefault();
3358         }
3359         //Roo.log('img onclick');
3360         this.fireEvent('click', this, e);
3361     }
3362    
3363 });
3364
3365  /*
3366  * - LGPL
3367  *
3368  * header
3369  * 
3370  */
3371
3372 /**
3373  * @class Roo.bootstrap.Header
3374  * @extends Roo.bootstrap.Component
3375  * Bootstrap Header class
3376  * @cfg {String} html content of header
3377  * @cfg {Number} level (1|2|3|4|5|6) default 1
3378  * 
3379  * @constructor
3380  * Create a new Header
3381  * @param {Object} config The config object
3382  */
3383
3384
3385 Roo.bootstrap.Header  = function(config){
3386     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3387 };
3388
3389 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3390     
3391     //href : false,
3392     html : false,
3393     level : 1,
3394     
3395     
3396     
3397     getAutoCreate : function(){
3398         
3399         
3400         
3401         var cfg = {
3402             tag: 'h' + (1 *this.level),
3403             html: this.html || ''
3404         } ;
3405         
3406         return cfg;
3407     }
3408    
3409 });
3410
3411  
3412
3413  /*
3414  * Based on:
3415  * Ext JS Library 1.1.1
3416  * Copyright(c) 2006-2007, Ext JS, LLC.
3417  *
3418  * Originally Released Under LGPL - original licence link has changed is not relivant.
3419  *
3420  * Fork - LGPL
3421  * <script type="text/javascript">
3422  */
3423  
3424 /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3427  * @singleton
3428  */
3429 Roo.bootstrap.MenuMgr = function(){
3430    var menus, active, groups = {}, attached = false, lastShow = new Date();
3431
3432    // private - called when first menu is created
3433    function init(){
3434        menus = {};
3435        active = new Roo.util.MixedCollection();
3436        Roo.get(document).addKeyListener(27, function(){
3437            if(active.length > 0){
3438                hideAll();
3439            }
3440        });
3441    }
3442
3443    // private
3444    function hideAll(){
3445        if(active && active.length > 0){
3446            var c = active.clone();
3447            c.each(function(m){
3448                m.hide();
3449            });
3450        }
3451    }
3452
3453    // private
3454    function onHide(m){
3455        active.remove(m);
3456        if(active.length < 1){
3457            Roo.get(document).un("mouseup", onMouseDown);
3458             
3459            attached = false;
3460        }
3461    }
3462
3463    // private
3464    function onShow(m){
3465        var last = active.last();
3466        lastShow = new Date();
3467        active.add(m);
3468        if(!attached){
3469           Roo.get(document).on("mouseup", onMouseDown);
3470            
3471            attached = true;
3472        }
3473        if(m.parentMenu){
3474           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3475           m.parentMenu.activeChild = m;
3476        }else if(last && last.isVisible()){
3477           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3478        }
3479    }
3480
3481    // private
3482    function onBeforeHide(m){
3483        if(m.activeChild){
3484            m.activeChild.hide();
3485        }
3486        if(m.autoHideTimer){
3487            clearTimeout(m.autoHideTimer);
3488            delete m.autoHideTimer;
3489        }
3490    }
3491
3492    // private
3493    function onBeforeShow(m){
3494        var pm = m.parentMenu;
3495        if(!pm && !m.allowOtherMenus){
3496            hideAll();
3497        }else if(pm && pm.activeChild && active != m){
3498            pm.activeChild.hide();
3499        }
3500    }
3501
3502    // private this should really trigger on mouseup..
3503    function onMouseDown(e){
3504         Roo.log("on Mouse Up");
3505         
3506         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3507             Roo.log("MenuManager hideAll");
3508             hideAll();
3509             e.stopEvent();
3510         }
3511         
3512         
3513    }
3514
3515    // private
3516    function onBeforeCheck(mi, state){
3517        if(state){
3518            var g = groups[mi.group];
3519            for(var i = 0, l = g.length; i < l; i++){
3520                if(g[i] != mi){
3521                    g[i].setChecked(false);
3522                }
3523            }
3524        }
3525    }
3526
3527    return {
3528
3529        /**
3530         * Hides all menus that are currently visible
3531         */
3532        hideAll : function(){
3533             hideAll();  
3534        },
3535
3536        // private
3537        register : function(menu){
3538            if(!menus){
3539                init();
3540            }
3541            menus[menu.id] = menu;
3542            menu.on("beforehide", onBeforeHide);
3543            menu.on("hide", onHide);
3544            menu.on("beforeshow", onBeforeShow);
3545            menu.on("show", onShow);
3546            var g = menu.group;
3547            if(g && menu.events["checkchange"]){
3548                if(!groups[g]){
3549                    groups[g] = [];
3550                }
3551                groups[g].push(menu);
3552                menu.on("checkchange", onCheck);
3553            }
3554        },
3555
3556         /**
3557          * Returns a {@link Roo.menu.Menu} object
3558          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3559          * be used to generate and return a new Menu instance.
3560          */
3561        get : function(menu){
3562            if(typeof menu == "string"){ // menu id
3563                return menus[menu];
3564            }else if(menu.events){  // menu instance
3565                return menu;
3566            }
3567            /*else if(typeof menu.length == 'number'){ // array of menu items?
3568                return new Roo.bootstrap.Menu({items:menu});
3569            }else{ // otherwise, must be a config
3570                return new Roo.bootstrap.Menu(menu);
3571            }
3572            */
3573            return false;
3574        },
3575
3576        // private
3577        unregister : function(menu){
3578            delete menus[menu.id];
3579            menu.un("beforehide", onBeforeHide);
3580            menu.un("hide", onHide);
3581            menu.un("beforeshow", onBeforeShow);
3582            menu.un("show", onShow);
3583            var g = menu.group;
3584            if(g && menu.events["checkchange"]){
3585                groups[g].remove(menu);
3586                menu.un("checkchange", onCheck);
3587            }
3588        },
3589
3590        // private
3591        registerCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                if(!groups[g]){
3595                    groups[g] = [];
3596                }
3597                groups[g].push(menuItem);
3598                menuItem.on("beforecheckchange", onBeforeCheck);
3599            }
3600        },
3601
3602        // private
3603        unregisterCheckable : function(menuItem){
3604            var g = menuItem.group;
3605            if(g){
3606                groups[g].remove(menuItem);
3607                menuItem.un("beforecheckchange", onBeforeCheck);
3608            }
3609        }
3610    };
3611 }();/*
3612  * - LGPL
3613  *
3614  * menu
3615  * 
3616  */
3617
3618 /**
3619  * @class Roo.bootstrap.Menu
3620  * @extends Roo.bootstrap.Component
3621  * Bootstrap Menu class - container for MenuItems
3622  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3623  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3624  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3625  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3626   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3627   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3628  
3629  * @constructor
3630  * Create a new Menu
3631  * @param {Object} config The config object
3632  */
3633
3634
3635 Roo.bootstrap.Menu = function(config){
3636     
3637     if (config.type == 'treeview') {
3638         // normally menu's are drawn attached to the document to handle layering etc..
3639         // however treeview (used by the docs menu is drawn into the parent element)
3640         this.container_method = 'getChildContainer'; 
3641     }
3642     
3643     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3644     if (this.registerMenu && this.type != 'treeview')  {
3645         Roo.bootstrap.MenuMgr.register(this);
3646     }
3647     
3648     
3649     this.addEvents({
3650         /**
3651          * @event beforeshow
3652          * Fires before this menu is displayed (return false to block)
3653          * @param {Roo.menu.Menu} this
3654          */
3655         beforeshow : true,
3656         /**
3657          * @event beforehide
3658          * Fires before this menu is hidden (return false to block)
3659          * @param {Roo.menu.Menu} this
3660          */
3661         beforehide : true,
3662         /**
3663          * @event show
3664          * Fires after this menu is displayed
3665          * @param {Roo.menu.Menu} this
3666          */
3667         show : true,
3668         /**
3669          * @event hide
3670          * Fires after this menu is hidden
3671          * @param {Roo.menu.Menu} this
3672          */
3673         hide : true,
3674         /**
3675          * @event click
3676          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3677          * @param {Roo.menu.Menu} this
3678          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3679          * @param {Roo.EventObject} e
3680          */
3681         click : true,
3682         /**
3683          * @event mouseover
3684          * Fires when the mouse is hovering over this menu
3685          * @param {Roo.menu.Menu} this
3686          * @param {Roo.EventObject} e
3687          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688          */
3689         mouseover : true,
3690         /**
3691          * @event mouseout
3692          * Fires when the mouse exits this menu
3693          * @param {Roo.menu.Menu} this
3694          * @param {Roo.EventObject} e
3695          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3696          */
3697         mouseout : true,
3698         /**
3699          * @event itemclick
3700          * Fires when a menu item contained in this menu is clicked
3701          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3702          * @param {Roo.EventObject} e
3703          */
3704         itemclick: true
3705     });
3706     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3707 };
3708
3709 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3710     
3711    /// html : false,
3712    
3713     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3714     type: false,
3715     /**
3716      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3717      */
3718     registerMenu : true,
3719     
3720     menuItems :false, // stores the menu items..
3721     
3722     hidden:true,
3723         
3724     parentMenu : false,
3725     
3726     stopEvent : true,
3727     
3728     isLink : false,
3729     
3730     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3731     
3732     hideTrigger : false,
3733     
3734     align : 'tl-bl?',
3735     
3736     
3737     getChildContainer : function() {
3738         return this.el;  
3739     },
3740     
3741     getAutoCreate : function(){
3742          
3743         //if (['right'].indexOf(this.align)!==-1) {
3744         //    cfg.cn[1].cls += ' pull-right'
3745         //}
3746          
3747         var cfg = {
3748             tag : 'ul',
3749             cls : 'dropdown-menu shadow' ,
3750             style : 'z-index:1000'
3751             
3752         };
3753         
3754         if (this.type === 'submenu') {
3755             cfg.cls = 'submenu active';
3756         }
3757         if (this.type === 'treeview') {
3758             cfg.cls = 'treeview-menu';
3759         }
3760         
3761         return cfg;
3762     },
3763     initEvents : function() {
3764         
3765        // Roo.log("ADD event");
3766        // Roo.log(this.triggerEl.dom);
3767         if (this.triggerEl) {
3768             
3769             this.triggerEl.on('click', this.onTriggerClick, this);
3770             
3771             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3772             
3773             if (!this.hideTrigger) {
3774                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3775                     // dropdown toggle on the 'a' in BS4?
3776                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3777                 } else {
3778                     this.triggerEl.addClass('dropdown-toggle');
3779                 }
3780             }
3781         }
3782         
3783         if (Roo.isTouch) {
3784             this.el.on('touchstart'  , this.onTouch, this);
3785         }
3786         this.el.on('click' , this.onClick, this);
3787
3788         this.el.on("mouseover", this.onMouseOver, this);
3789         this.el.on("mouseout", this.onMouseOut, this);
3790         
3791     },
3792     
3793     findTargetItem : function(e)
3794     {
3795         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3796         if(!t){
3797             return false;
3798         }
3799         //Roo.log(t);         Roo.log(t.id);
3800         if(t && t.id){
3801             //Roo.log(this.menuitems);
3802             return this.menuitems.get(t.id);
3803             
3804             //return this.items.get(t.menuItemId);
3805         }
3806         
3807         return false;
3808     },
3809     
3810     onTouch : function(e) 
3811     {
3812         Roo.log("menu.onTouch");
3813         //e.stopEvent(); this make the user popdown broken
3814         this.onClick(e);
3815     },
3816     
3817     onClick : function(e)
3818     {
3819         Roo.log("menu.onClick");
3820         
3821         var t = this.findTargetItem(e);
3822         if(!t || t.isContainer){
3823             return;
3824         }
3825         Roo.log(e);
3826         /*
3827         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3828             if(t == this.activeItem && t.shouldDeactivate(e)){
3829                 this.activeItem.deactivate();
3830                 delete this.activeItem;
3831                 return;
3832             }
3833             if(t.canActivate){
3834                 this.setActiveItem(t, true);
3835             }
3836             return;
3837             
3838             
3839         }
3840         */
3841        
3842         Roo.log('pass click event');
3843         
3844         t.onClick(e);
3845         
3846         this.fireEvent("click", this, t, e);
3847         
3848         var _this = this;
3849         
3850         if(!t.href.length || t.href == '#'){
3851             (function() { _this.hide(); }).defer(100);
3852         }
3853         
3854     },
3855     
3856     onMouseOver : function(e){
3857         var t  = this.findTargetItem(e);
3858         //Roo.log(t);
3859         //if(t){
3860         //    if(t.canActivate && !t.disabled){
3861         //        this.setActiveItem(t, true);
3862         //    }
3863         //}
3864         
3865         this.fireEvent("mouseover", this, e, t);
3866     },
3867     isVisible : function(){
3868         return !this.hidden;
3869     },
3870     onMouseOut : function(e){
3871         var t  = this.findTargetItem(e);
3872         
3873         //if(t ){
3874         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3875         //        this.activeItem.deactivate();
3876         //        delete this.activeItem;
3877         //    }
3878         //}
3879         this.fireEvent("mouseout", this, e, t);
3880     },
3881     
3882     
3883     /**
3884      * Displays this menu relative to another element
3885      * @param {String/HTMLElement/Roo.Element} element The element to align to
3886      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3887      * the element (defaults to this.defaultAlign)
3888      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3889      */
3890     show : function(el, pos, parentMenu)
3891     {
3892         if (false === this.fireEvent("beforeshow", this)) {
3893             Roo.log("show canceled");
3894             return;
3895         }
3896         this.parentMenu = parentMenu;
3897         if(!this.el){
3898             this.render();
3899         }
3900         this.el.addClass('show'); // show otherwise we do not know how big we are..
3901          
3902         var xy = this.el.getAlignToXY(el, pos);
3903         
3904         // bl-tl << left align  below
3905         // tl-bl << left align 
3906         
3907         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3908             // if it goes to far to the right.. -> align left.
3909             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3910         }
3911         if(xy[0] < 0){
3912             // was left align - go right?
3913             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3914         }
3915         
3916         // goes down the bottom
3917         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3918            xy[1]  < 0 ){
3919             var a = this.align.replace('?', '').split('-');
3920             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3921             
3922         }
3923         
3924         this.showAt(  xy , parentMenu, false);
3925     },
3926      /**
3927      * Displays this menu at a specific xy position
3928      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3929      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3930      */
3931     showAt : function(xy, parentMenu, /* private: */_e){
3932         this.parentMenu = parentMenu;
3933         if(!this.el){
3934             this.render();
3935         }
3936         if(_e !== false){
3937             this.fireEvent("beforeshow", this);
3938             //xy = this.el.adjustForConstraints(xy);
3939         }
3940         
3941         //this.el.show();
3942         this.hideMenuItems();
3943         this.hidden = false;
3944         if (this.triggerEl) {
3945             this.triggerEl.addClass('open');
3946         }
3947         
3948         this.el.addClass('show');
3949         
3950         
3951         
3952         // reassign x when hitting right
3953         
3954         // reassign y when hitting bottom
3955         
3956         // but the list may align on trigger left or trigger top... should it be a properity?
3957         
3958         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3959             this.el.setXY(xy);
3960         }
3961         
3962         this.focus();
3963         this.fireEvent("show", this);
3964     },
3965     
3966     focus : function(){
3967         return;
3968         if(!this.hidden){
3969             this.doFocus.defer(50, this);
3970         }
3971     },
3972
3973     doFocus : function(){
3974         if(!this.hidden){
3975             this.focusEl.focus();
3976         }
3977     },
3978
3979     /**
3980      * Hides this menu and optionally all parent menus
3981      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3982      */
3983     hide : function(deep)
3984     {
3985         if (false === this.fireEvent("beforehide", this)) {
3986             Roo.log("hide canceled");
3987             return;
3988         }
3989         this.hideMenuItems();
3990         if(this.el && this.isVisible()){
3991            
3992             if(this.activeItem){
3993                 this.activeItem.deactivate();
3994                 this.activeItem = null;
3995             }
3996             if (this.triggerEl) {
3997                 this.triggerEl.removeClass('open');
3998             }
3999             
4000             this.el.removeClass('show');
4001             this.hidden = true;
4002             this.fireEvent("hide", this);
4003         }
4004         if(deep === true && this.parentMenu){
4005             this.parentMenu.hide(true);
4006         }
4007     },
4008     
4009     onTriggerClick : function(e)
4010     {
4011         Roo.log('trigger click');
4012         
4013         var target = e.getTarget();
4014         
4015         Roo.log(target.nodeName.toLowerCase());
4016         
4017         if(target.nodeName.toLowerCase() === 'i'){
4018             e.preventDefault();
4019         }
4020         
4021     },
4022     
4023     onTriggerPress  : function(e)
4024     {
4025         Roo.log('trigger press');
4026         //Roo.log(e.getTarget());
4027        // Roo.log(this.triggerEl.dom);
4028        
4029         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4030         var pel = Roo.get(e.getTarget());
4031         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4032             Roo.log('is treeview or dropdown?');
4033             return;
4034         }
4035         
4036         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4037             return;
4038         }
4039         
4040         if (this.isVisible()) {
4041             Roo.log('hide');
4042             this.hide();
4043         } else {
4044             Roo.log('show');
4045             
4046             this.show(this.triggerEl, this.align, false);
4047         }
4048         
4049         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4050             e.stopEvent();
4051         }
4052         
4053     },
4054        
4055     
4056     hideMenuItems : function()
4057     {
4058         Roo.log("hide Menu Items");
4059         if (!this.el) { 
4060             return;
4061         }
4062         
4063         this.el.select('.open',true).each(function(aa) {
4064             
4065             aa.removeClass('open');
4066          
4067         });
4068     },
4069     addxtypeChild : function (tree, cntr) {
4070         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4071           
4072         this.menuitems.add(comp);
4073         return comp;
4074
4075     },
4076     getEl : function()
4077     {
4078         Roo.log(this.el);
4079         return this.el;
4080     },
4081     
4082     clear : function()
4083     {
4084         this.getEl().dom.innerHTML = '';
4085         this.menuitems.clear();
4086     }
4087 });
4088
4089  
4090  /*
4091  * - LGPL
4092  *
4093  * menu item
4094  * 
4095  */
4096
4097
4098 /**
4099  * @class Roo.bootstrap.MenuItem
4100  * @extends Roo.bootstrap.Component
4101  * Bootstrap MenuItem class
4102  * @cfg {String} html the menu label
4103  * @cfg {String} href the link
4104  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4105  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4106  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4107  * @cfg {String} fa favicon to show on left of menu item.
4108  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4109  * 
4110  * 
4111  * @constructor
4112  * Create a new MenuItem
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.MenuItem = function(config){
4118     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4119     this.addEvents({
4120         // raw events
4121         /**
4122          * @event click
4123          * The raw click event for the entire grid.
4124          * @param {Roo.bootstrap.MenuItem} this
4125          * @param {Roo.EventObject} e
4126          */
4127         "click" : true
4128     });
4129 };
4130
4131 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4132     
4133     href : false,
4134     html : false,
4135     preventDefault: false,
4136     isContainer : false,
4137     active : false,
4138     fa: false,
4139     
4140     getAutoCreate : function(){
4141         
4142         if(this.isContainer){
4143             return {
4144                 tag: 'li',
4145                 cls: 'dropdown-menu-item '
4146             };
4147         }
4148         var ctag = {
4149             tag: 'span',
4150             html: 'Link'
4151         };
4152         
4153         var anc = {
4154             tag : 'a',
4155             cls : 'dropdown-item',
4156             href : '#',
4157             cn : [  ]
4158         };
4159         
4160         if (this.fa !== false) {
4161             anc.cn.push({
4162                 tag : 'i',
4163                 cls : 'fa fa-' + this.fa
4164             });
4165         }
4166         
4167         anc.cn.push(ctag);
4168         
4169         
4170         var cfg= {
4171             tag: 'li',
4172             cls: 'dropdown-menu-item',
4173             cn: [ anc ]
4174         };
4175         if (this.parent().type == 'treeview') {
4176             cfg.cls = 'treeview-menu';
4177         }
4178         if (this.active) {
4179             cfg.cls += ' active';
4180         }
4181         
4182         
4183         
4184         anc.href = this.href || cfg.cn[0].href ;
4185         ctag.html = this.html || cfg.cn[0].html ;
4186         return cfg;
4187     },
4188     
4189     initEvents: function()
4190     {
4191         if (this.parent().type == 'treeview') {
4192             this.el.select('a').on('click', this.onClick, this);
4193         }
4194         
4195         if (this.menu) {
4196             this.menu.parentType = this.xtype;
4197             this.menu.triggerEl = this.el;
4198             this.menu = this.addxtype(Roo.apply({}, this.menu));
4199         }
4200         
4201     },
4202     onClick : function(e)
4203     {
4204         Roo.log('item on click ');
4205         
4206         if(this.preventDefault){
4207             e.preventDefault();
4208         }
4209         //this.parent().hideMenuItems();
4210         
4211         this.fireEvent('click', this, e);
4212     },
4213     getEl : function()
4214     {
4215         return this.el;
4216     } 
4217 });
4218
4219  
4220
4221  /*
4222  * - LGPL
4223  *
4224  * menu separator
4225  * 
4226  */
4227
4228
4229 /**
4230  * @class Roo.bootstrap.MenuSeparator
4231  * @extends Roo.bootstrap.Component
4232  * Bootstrap MenuSeparator class
4233  * 
4234  * @constructor
4235  * Create a new MenuItem
4236  * @param {Object} config The config object
4237  */
4238
4239
4240 Roo.bootstrap.MenuSeparator = function(config){
4241     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4242 };
4243
4244 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4245     
4246     getAutoCreate : function(){
4247         var cfg = {
4248             cls: 'divider',
4249             tag : 'li'
4250         };
4251         
4252         return cfg;
4253     }
4254    
4255 });
4256
4257  
4258
4259  
4260 /*
4261 * Licence: LGPL
4262 */
4263
4264 /**
4265  * @class Roo.bootstrap.Modal
4266  * @extends Roo.bootstrap.Component
4267  * @builder-top
4268  * @parent none
4269  * @children Roo.bootstrap.Component
4270  * Bootstrap Modal class
4271  * @cfg {String} title Title of dialog
4272  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4273  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4274  * @cfg {Boolean} specificTitle default false
4275  * @cfg {Array} buttons Array of buttons or standard button set..
4276  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4277  * @cfg {Boolean} animate default true
4278  * @cfg {Boolean} allow_close default true
4279  * @cfg {Boolean} fitwindow default false
4280  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4281  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4282  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4283  * @cfg {String} size (sm|lg|xl) default empty
4284  * @cfg {Number} max_width set the max width of modal
4285  * @cfg {Boolean} editableTitle can the title be edited
4286
4287  *
4288  *
4289  * @constructor
4290  * Create a new Modal Dialog
4291  * @param {Object} config The config object
4292  */
4293
4294 Roo.bootstrap.Modal = function(config){
4295     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4296     this.addEvents({
4297         // raw events
4298         /**
4299          * @event btnclick
4300          * The raw btnclick event for the button
4301          * @param {Roo.EventObject} e
4302          */
4303         "btnclick" : true,
4304         /**
4305          * @event resize
4306          * Fire when dialog resize
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} e
4309          */
4310         "resize" : true,
4311         /**
4312          * @event titlechanged
4313          * Fire when the editable title has been changed
4314          * @param {Roo.bootstrap.Modal} this
4315          * @param {Roo.EventObject} value
4316          */
4317         "titlechanged" : true 
4318         
4319     });
4320     this.buttons = this.buttons || [];
4321
4322     if (this.tmpl) {
4323         this.tmpl = Roo.factory(this.tmpl);
4324     }
4325
4326 };
4327
4328 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4329
4330     title : 'test dialog',
4331
4332     buttons : false,
4333
4334     // set on load...
4335
4336     html: false,
4337
4338     tmp: false,
4339
4340     specificTitle: false,
4341
4342     buttonPosition: 'right',
4343
4344     allow_close : true,
4345
4346     animate : true,
4347
4348     fitwindow: false,
4349     
4350      // private
4351     dialogEl: false,
4352     bodyEl:  false,
4353     footerEl:  false,
4354     titleEl:  false,
4355     closeEl:  false,
4356
4357     size: '',
4358     
4359     max_width: 0,
4360     
4361     max_height: 0,
4362     
4363     fit_content: false,
4364     editableTitle  : false,
4365
4366     onRender : function(ct, position)
4367     {
4368         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4369
4370         if(!this.el){
4371             var cfg = Roo.apply({},  this.getAutoCreate());
4372             cfg.id = Roo.id();
4373             //if(!cfg.name){
4374             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4375             //}
4376             //if (!cfg.name.length) {
4377             //    delete cfg.name;
4378            // }
4379             if (this.cls) {
4380                 cfg.cls += ' ' + this.cls;
4381             }
4382             if (this.style) {
4383                 cfg.style = this.style;
4384             }
4385             this.el = Roo.get(document.body).createChild(cfg, position);
4386         }
4387         //var type = this.el.dom.type;
4388
4389
4390         if(this.tabIndex !== undefined){
4391             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4392         }
4393
4394         this.dialogEl = this.el.select('.modal-dialog',true).first();
4395         this.bodyEl = this.el.select('.modal-body',true).first();
4396         this.closeEl = this.el.select('.modal-header .close', true).first();
4397         this.headerEl = this.el.select('.modal-header',true).first();
4398         this.titleEl = this.el.select('.modal-title',true).first();
4399         this.footerEl = this.el.select('.modal-footer',true).first();
4400
4401         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4402         
4403         //this.el.addClass("x-dlg-modal");
4404
4405         if (this.buttons.length) {
4406             Roo.each(this.buttons, function(bb) {
4407                 var b = Roo.apply({}, bb);
4408                 b.xns = b.xns || Roo.bootstrap;
4409                 b.xtype = b.xtype || 'Button';
4410                 if (typeof(b.listeners) == 'undefined') {
4411                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4412                 }
4413
4414                 var btn = Roo.factory(b);
4415
4416                 btn.render(this.getButtonContainer());
4417
4418             },this);
4419         }
4420         // render the children.
4421         var nitems = [];
4422
4423         if(typeof(this.items) != 'undefined'){
4424             var items = this.items;
4425             delete this.items;
4426
4427             for(var i =0;i < items.length;i++) {
4428                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4429             }
4430         }
4431
4432         this.items = nitems;
4433
4434         // where are these used - they used to be body/close/footer
4435
4436
4437         this.initEvents();
4438         //this.el.addClass([this.fieldClass, this.cls]);
4439
4440     },
4441
4442     getAutoCreate : function()
4443     {
4444         // we will default to modal-body-overflow - might need to remove or make optional later.
4445         var bdy = {
4446                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4447                 html : this.html || ''
4448         };
4449
4450         var title = {
4451             tag: 'h5',
4452             cls : 'modal-title',
4453             html : this.title
4454         };
4455
4456         if(this.specificTitle){ // WTF is this?
4457             title = this.title;
4458         }
4459
4460         var header = [];
4461         if (this.allow_close && Roo.bootstrap.version == 3) {
4462             header.push({
4463                 tag: 'button',
4464                 cls : 'close',
4465                 html : '&times'
4466             });
4467         }
4468
4469         header.push(title);
4470
4471         if (this.editableTitle) {
4472             header.push({
4473                 cls: 'form-control roo-editable-title d-none',
4474                 tag: 'input',
4475                 type: 'text'
4476             });
4477         }
4478         
4479         if (this.allow_close && Roo.bootstrap.version == 4) {
4480             header.push({
4481                 tag: 'button',
4482                 cls : 'close',
4483                 html : '&times'
4484             });
4485         }
4486         
4487         var size = '';
4488
4489         if(this.size.length){
4490             size = 'modal-' + this.size;
4491         }
4492         
4493         var footer = Roo.bootstrap.version == 3 ?
4494             {
4495                 cls : 'modal-footer',
4496                 cn : [
4497                     {
4498                         tag: 'div',
4499                         cls: 'btn-' + this.buttonPosition
4500                     }
4501                 ]
4502
4503             } :
4504             {  // BS4 uses mr-auto on left buttons....
4505                 cls : 'modal-footer'
4506             };
4507
4508             
4509
4510         
4511         
4512         var modal = {
4513             cls: "modal",
4514              cn : [
4515                 {
4516                     cls: "modal-dialog " + size,
4517                     cn : [
4518                         {
4519                             cls : "modal-content",
4520                             cn : [
4521                                 {
4522                                     cls : 'modal-header',
4523                                     cn : header
4524                                 },
4525                                 bdy,
4526                                 footer
4527                             ]
4528
4529                         }
4530                     ]
4531
4532                 }
4533             ]
4534         };
4535
4536         if(this.animate){
4537             modal.cls += ' fade';
4538         }
4539
4540         return modal;
4541
4542     },
4543     getChildContainer : function() {
4544
4545          return this.bodyEl;
4546
4547     },
4548     getButtonContainer : function() {
4549         
4550          return Roo.bootstrap.version == 4 ?
4551             this.el.select('.modal-footer',true).first()
4552             : this.el.select('.modal-footer div',true).first();
4553
4554     },
4555     initEvents : function()
4556     {
4557         if (this.allow_close) {
4558             this.closeEl.on('click', this.hide, this);
4559         }
4560         Roo.EventManager.onWindowResize(this.resize, this, true);
4561         if (this.editableTitle) {
4562             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4563             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4564             this.headerEditEl.on('keyup', function(e) {
4565                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4566                         this.toggleHeaderInput(false)
4567                     }
4568                 }, this);
4569             this.headerEditEl.on('blur', function(e) {
4570                 this.toggleHeaderInput(false)
4571             },this);
4572         }
4573
4574     },
4575   
4576
4577     resize : function()
4578     {
4579         this.maskEl.setSize(
4580             Roo.lib.Dom.getViewWidth(true),
4581             Roo.lib.Dom.getViewHeight(true)
4582         );
4583         
4584         if (this.fitwindow) {
4585             
4586            this.dialogEl.setStyle( { 'max-width' : '100%' });
4587             this.setSize(
4588                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4589                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4590             );
4591             return;
4592         }
4593         
4594         if(this.max_width !== 0) {
4595             
4596             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4597             
4598             if(this.height) {
4599                 this.setSize(w, this.height);
4600                 return;
4601             }
4602             
4603             if(this.max_height) {
4604                 this.setSize(w,Math.min(
4605                     this.max_height,
4606                     Roo.lib.Dom.getViewportHeight(true) - 60
4607                 ));
4608                 
4609                 return;
4610             }
4611             
4612             if(!this.fit_content) {
4613                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4614                 return;
4615             }
4616             
4617             this.setSize(w, Math.min(
4618                 60 +
4619                 this.headerEl.getHeight() + 
4620                 this.footerEl.getHeight() + 
4621                 this.getChildHeight(this.bodyEl.dom.childNodes),
4622                 Roo.lib.Dom.getViewportHeight(true) - 60)
4623             );
4624         }
4625         
4626     },
4627
4628     setSize : function(w,h)
4629     {
4630         if (!w && !h) {
4631             return;
4632         }
4633         
4634         this.resizeTo(w,h);
4635     },
4636
4637     show : function() {
4638
4639         if (!this.rendered) {
4640             this.render();
4641         }
4642         this.toggleHeaderInput(false);
4643         //this.el.setStyle('display', 'block');
4644         this.el.removeClass('hideing');
4645         this.el.dom.style.display='block';
4646         
4647         Roo.get(document.body).addClass('modal-open');
4648  
4649         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4650             
4651             (function(){
4652                 this.el.addClass('show');
4653                 this.el.addClass('in');
4654             }).defer(50, this);
4655         }else{
4656             this.el.addClass('show');
4657             this.el.addClass('in');
4658         }
4659
4660         // not sure how we can show data in here..
4661         //if (this.tmpl) {
4662         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4663         //}
4664
4665         Roo.get(document.body).addClass("x-body-masked");
4666         
4667         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4668         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4669         this.maskEl.dom.style.display = 'block';
4670         this.maskEl.addClass('show');
4671         
4672         
4673         this.resize();
4674         
4675         this.fireEvent('show', this);
4676
4677         // set zindex here - otherwise it appears to be ignored...
4678         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4679
4680         (function () {
4681             this.items.forEach( function(e) {
4682                 e.layout ? e.layout() : false;
4683
4684             });
4685         }).defer(100,this);
4686
4687     },
4688     hide : function()
4689     {
4690         if(this.fireEvent("beforehide", this) !== false){
4691             
4692             this.maskEl.removeClass('show');
4693             
4694             this.maskEl.dom.style.display = '';
4695             Roo.get(document.body).removeClass("x-body-masked");
4696             this.el.removeClass('in');
4697             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4698
4699             if(this.animate){ // why
4700                 this.el.addClass('hideing');
4701                 this.el.removeClass('show');
4702                 (function(){
4703                     if (!this.el.hasClass('hideing')) {
4704                         return; // it's been shown again...
4705                     }
4706                     
4707                     this.el.dom.style.display='';
4708
4709                     Roo.get(document.body).removeClass('modal-open');
4710                     this.el.removeClass('hideing');
4711                 }).defer(150,this);
4712                 
4713             }else{
4714                 this.el.removeClass('show');
4715                 this.el.dom.style.display='';
4716                 Roo.get(document.body).removeClass('modal-open');
4717
4718             }
4719             this.fireEvent('hide', this);
4720         }
4721     },
4722     isVisible : function()
4723     {
4724         
4725         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4726         
4727     },
4728
4729     addButton : function(str, cb)
4730     {
4731
4732
4733         var b = Roo.apply({}, { html : str } );
4734         b.xns = b.xns || Roo.bootstrap;
4735         b.xtype = b.xtype || 'Button';
4736         if (typeof(b.listeners) == 'undefined') {
4737             b.listeners = { click : cb.createDelegate(this)  };
4738         }
4739
4740         var btn = Roo.factory(b);
4741
4742         btn.render(this.getButtonContainer());
4743
4744         return btn;
4745
4746     },
4747
4748     setDefaultButton : function(btn)
4749     {
4750         //this.el.select('.modal-footer').()
4751     },
4752
4753     resizeTo: function(w,h)
4754     {
4755         this.dialogEl.setWidth(w);
4756         
4757         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4758
4759         this.bodyEl.setHeight(h - diff);
4760         
4761         this.fireEvent('resize', this);
4762     },
4763     
4764     setContentSize  : function(w, h)
4765     {
4766
4767     },
4768     onButtonClick: function(btn,e)
4769     {
4770         //Roo.log([a,b,c]);
4771         this.fireEvent('btnclick', btn.name, e);
4772     },
4773      /**
4774      * Set the title of the Dialog
4775      * @param {String} str new Title
4776      */
4777     setTitle: function(str) {
4778         this.titleEl.dom.innerHTML = str;
4779         this.title = str;
4780     },
4781     /**
4782      * Set the body of the Dialog
4783      * @param {String} str new Title
4784      */
4785     setBody: function(str) {
4786         this.bodyEl.dom.innerHTML = str;
4787     },
4788     /**
4789      * Set the body of the Dialog using the template
4790      * @param {Obj} data - apply this data to the template and replace the body contents.
4791      */
4792     applyBody: function(obj)
4793     {
4794         if (!this.tmpl) {
4795             Roo.log("Error - using apply Body without a template");
4796             //code
4797         }
4798         this.tmpl.overwrite(this.bodyEl, obj);
4799     },
4800     
4801     getChildHeight : function(child_nodes)
4802     {
4803         if(
4804             !child_nodes ||
4805             child_nodes.length == 0
4806         ) {
4807             return 0;
4808         }
4809         
4810         var child_height = 0;
4811         
4812         for(var i = 0; i < child_nodes.length; i++) {
4813             
4814             /*
4815             * for modal with tabs...
4816             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4817                 
4818                 var layout_childs = child_nodes[i].childNodes;
4819                 
4820                 for(var j = 0; j < layout_childs.length; j++) {
4821                     
4822                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4823                         
4824                         var layout_body_childs = layout_childs[j].childNodes;
4825                         
4826                         for(var k = 0; k < layout_body_childs.length; k++) {
4827                             
4828                             if(layout_body_childs[k].classList.contains('navbar')) {
4829                                 child_height += layout_body_childs[k].offsetHeight;
4830                                 continue;
4831                             }
4832                             
4833                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4834                                 
4835                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4836                                 
4837                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4838                                     
4839                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4840                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4841                                         continue;
4842                                     }
4843                                     
4844                                 }
4845                                 
4846                             }
4847                             
4848                         }
4849                     }
4850                 }
4851                 continue;
4852             }
4853             */
4854             
4855             child_height += child_nodes[i].offsetHeight;
4856             // Roo.log(child_nodes[i].offsetHeight);
4857         }
4858         
4859         return child_height;
4860     },
4861     toggleHeaderInput : function(is_edit)
4862     {
4863         if (!this.editableTitle) {
4864             return; // not editable.
4865         }
4866         if (is_edit && this.is_header_editing) {
4867             return; // already editing..
4868         }
4869         if (is_edit) {
4870     
4871             this.headerEditEl.dom.value = this.title;
4872             this.headerEditEl.removeClass('d-none');
4873             this.headerEditEl.dom.focus();
4874             this.titleEl.addClass('d-none');
4875             
4876             this.is_header_editing = true;
4877             return
4878         }
4879         // flip back to not editing.
4880         this.title = this.headerEditEl.dom.value;
4881         this.headerEditEl.addClass('d-none');
4882         this.titleEl.removeClass('d-none');
4883         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4884         this.is_header_editing = false;
4885         this.fireEvent('titlechanged', this, this.title);
4886     
4887             
4888         
4889     }
4890
4891 });
4892
4893
4894 Roo.apply(Roo.bootstrap.Modal,  {
4895     /**
4896          * Button config that displays a single OK button
4897          * @type Object
4898          */
4899         OK :  [{
4900             name : 'ok',
4901             weight : 'primary',
4902             html : 'OK'
4903         }],
4904         /**
4905          * Button config that displays Yes and No buttons
4906          * @type Object
4907          */
4908         YESNO : [
4909             {
4910                 name  : 'no',
4911                 html : 'No'
4912             },
4913             {
4914                 name  :'yes',
4915                 weight : 'primary',
4916                 html : 'Yes'
4917             }
4918         ],
4919
4920         /**
4921          * Button config that displays OK and Cancel buttons
4922          * @type Object
4923          */
4924         OKCANCEL : [
4925             {
4926                name : 'cancel',
4927                 html : 'Cancel'
4928             },
4929             {
4930                 name : 'ok',
4931                 weight : 'primary',
4932                 html : 'OK'
4933             }
4934         ],
4935         /**
4936          * Button config that displays Yes, No and Cancel buttons
4937          * @type Object
4938          */
4939         YESNOCANCEL : [
4940             {
4941                 name : 'yes',
4942                 weight : 'primary',
4943                 html : 'Yes'
4944             },
4945             {
4946                 name : 'no',
4947                 html : 'No'
4948             },
4949             {
4950                 name : 'cancel',
4951                 html : 'Cancel'
4952             }
4953         ],
4954         
4955         zIndex : 10001
4956 });
4957
4958 /*
4959  * - LGPL
4960  *
4961  * messagebox - can be used as a replace
4962  * 
4963  */
4964 /**
4965  * @class Roo.MessageBox
4966  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4967  * Example usage:
4968  *<pre><code>
4969 // Basic alert:
4970 Roo.Msg.alert('Status', 'Changes saved successfully.');
4971
4972 // Prompt for user data:
4973 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4974     if (btn == 'ok'){
4975         // process text value...
4976     }
4977 });
4978
4979 // Show a dialog using config options:
4980 Roo.Msg.show({
4981    title:'Save Changes?',
4982    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4983    buttons: Roo.Msg.YESNOCANCEL,
4984    fn: processResult,
4985    animEl: 'elId'
4986 });
4987 </code></pre>
4988  * @singleton
4989  */
4990 Roo.bootstrap.MessageBox = function(){
4991     var dlg, opt, mask, waitTimer;
4992     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4993     var buttons, activeTextEl, bwidth;
4994
4995     
4996     // private
4997     var handleButton = function(button){
4998         dlg.hide();
4999         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5000     };
5001
5002     // private
5003     var handleHide = function(){
5004         if(opt && opt.cls){
5005             dlg.el.removeClass(opt.cls);
5006         }
5007         //if(waitTimer){
5008         //    Roo.TaskMgr.stop(waitTimer);
5009         //    waitTimer = null;
5010         //}
5011     };
5012
5013     // private
5014     var updateButtons = function(b){
5015         var width = 0;
5016         if(!b){
5017             buttons["ok"].hide();
5018             buttons["cancel"].hide();
5019             buttons["yes"].hide();
5020             buttons["no"].hide();
5021             dlg.footerEl.hide();
5022             
5023             return width;
5024         }
5025         dlg.footerEl.show();
5026         for(var k in buttons){
5027             if(typeof buttons[k] != "function"){
5028                 if(b[k]){
5029                     buttons[k].show();
5030                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5031                     width += buttons[k].el.getWidth()+15;
5032                 }else{
5033                     buttons[k].hide();
5034                 }
5035             }
5036         }
5037         return width;
5038     };
5039
5040     // private
5041     var handleEsc = function(d, k, e){
5042         if(opt && opt.closable !== false){
5043             dlg.hide();
5044         }
5045         if(e){
5046             e.stopEvent();
5047         }
5048     };
5049
5050     return {
5051         /**
5052          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5053          * @return {Roo.BasicDialog} The BasicDialog element
5054          */
5055         getDialog : function(){
5056            if(!dlg){
5057                 dlg = new Roo.bootstrap.Modal( {
5058                     //draggable: true,
5059                     //resizable:false,
5060                     //constraintoviewport:false,
5061                     //fixedcenter:true,
5062                     //collapsible : false,
5063                     //shim:true,
5064                     //modal: true,
5065                 //    width: 'auto',
5066                   //  height:100,
5067                     //buttonAlign:"center",
5068                     closeClick : function(){
5069                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5070                             handleButton("no");
5071                         }else{
5072                             handleButton("cancel");
5073                         }
5074                     }
5075                 });
5076                 dlg.render();
5077                 dlg.on("hide", handleHide);
5078                 mask = dlg.mask;
5079                 //dlg.addKeyListener(27, handleEsc);
5080                 buttons = {};
5081                 this.buttons = buttons;
5082                 var bt = this.buttonText;
5083                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5084                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5085                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5086                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5087                 //Roo.log(buttons);
5088                 bodyEl = dlg.bodyEl.createChild({
5089
5090                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5091                         '<textarea class="roo-mb-textarea"></textarea>' +
5092                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5093                 });
5094                 msgEl = bodyEl.dom.firstChild;
5095                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5096                 textboxEl.enableDisplayMode();
5097                 textboxEl.addKeyListener([10,13], function(){
5098                     if(dlg.isVisible() && opt && opt.buttons){
5099                         if(opt.buttons.ok){
5100                             handleButton("ok");
5101                         }else if(opt.buttons.yes){
5102                             handleButton("yes");
5103                         }
5104                     }
5105                 });
5106                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5107                 textareaEl.enableDisplayMode();
5108                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5109                 progressEl.enableDisplayMode();
5110                 
5111                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5112                 var pf = progressEl.dom.firstChild;
5113                 if (pf) {
5114                     pp = Roo.get(pf.firstChild);
5115                     pp.setHeight(pf.offsetHeight);
5116                 }
5117                 
5118             }
5119             return dlg;
5120         },
5121
5122         /**
5123          * Updates the message box body text
5124          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5125          * the XHTML-compliant non-breaking space character '&amp;#160;')
5126          * @return {Roo.MessageBox} This message box
5127          */
5128         updateText : function(text)
5129         {
5130             if(!dlg.isVisible() && !opt.width){
5131                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5132                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5133             }
5134             msgEl.innerHTML = text || '&#160;';
5135       
5136             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5137             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5138             var w = Math.max(
5139                     Math.min(opt.width || cw , this.maxWidth), 
5140                     Math.max(opt.minWidth || this.minWidth, bwidth)
5141             );
5142             if(opt.prompt){
5143                 activeTextEl.setWidth(w);
5144             }
5145             if(dlg.isVisible()){
5146                 dlg.fixedcenter = false;
5147             }
5148             // to big, make it scroll. = But as usual stupid IE does not support
5149             // !important..
5150             
5151             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5152                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5153                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5154             } else {
5155                 bodyEl.dom.style.height = '';
5156                 bodyEl.dom.style.overflowY = '';
5157             }
5158             if (cw > w) {
5159                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5160             } else {
5161                 bodyEl.dom.style.overflowX = '';
5162             }
5163             
5164             dlg.setContentSize(w, bodyEl.getHeight());
5165             if(dlg.isVisible()){
5166                 dlg.fixedcenter = true;
5167             }
5168             return this;
5169         },
5170
5171         /**
5172          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5173          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5174          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5175          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5176          * @return {Roo.MessageBox} This message box
5177          */
5178         updateProgress : function(value, text){
5179             if(text){
5180                 this.updateText(text);
5181             }
5182             
5183             if (pp) { // weird bug on my firefox - for some reason this is not defined
5184                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5185                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5186             }
5187             return this;
5188         },        
5189
5190         /**
5191          * Returns true if the message box is currently displayed
5192          * @return {Boolean} True if the message box is visible, else false
5193          */
5194         isVisible : function(){
5195             return dlg && dlg.isVisible();  
5196         },
5197
5198         /**
5199          * Hides the message box if it is displayed
5200          */
5201         hide : function(){
5202             if(this.isVisible()){
5203                 dlg.hide();
5204             }  
5205         },
5206
5207         /**
5208          * Displays a new message box, or reinitializes an existing message box, based on the config options
5209          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5210          * The following config object properties are supported:
5211          * <pre>
5212 Property    Type             Description
5213 ----------  ---------------  ------------------------------------------------------------------------------------
5214 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5215                                    closes (defaults to undefined)
5216 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5217                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5218 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5219                                    progress and wait dialogs will ignore this property and always hide the
5220                                    close button as they can only be closed programmatically.
5221 cls               String           A custom CSS class to apply to the message box element
5222 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5223                                    displayed (defaults to 75)
5224 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5225                                    function will be btn (the name of the button that was clicked, if applicable,
5226                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5227                                    Progress and wait dialogs will ignore this option since they do not respond to
5228                                    user actions and can only be closed programmatically, so any required function
5229                                    should be called by the same code after it closes the dialog.
5230 icon              String           A CSS class that provides a background image to be used as an icon for
5231                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5232 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5233 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5234 modal             Boolean          False to allow user interaction with the page while the message box is
5235                                    displayed (defaults to true)
5236 msg               String           A string that will replace the existing message box body text (defaults
5237                                    to the XHTML-compliant non-breaking space character '&#160;')
5238 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5239 progress          Boolean          True to display a progress bar (defaults to false)
5240 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5241 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5242 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5243 title             String           The title text
5244 value             String           The string value to set into the active textbox element if displayed
5245 wait              Boolean          True to display a progress bar (defaults to false)
5246 width             Number           The width of the dialog in pixels
5247 </pre>
5248          *
5249          * Example usage:
5250          * <pre><code>
5251 Roo.Msg.show({
5252    title: 'Address',
5253    msg: 'Please enter your address:',
5254    width: 300,
5255    buttons: Roo.MessageBox.OKCANCEL,
5256    multiline: true,
5257    fn: saveAddress,
5258    animEl: 'addAddressBtn'
5259 });
5260 </code></pre>
5261          * @param {Object} config Configuration options
5262          * @return {Roo.MessageBox} This message box
5263          */
5264         show : function(options)
5265         {
5266             
5267             // this causes nightmares if you show one dialog after another
5268             // especially on callbacks..
5269              
5270             if(this.isVisible()){
5271                 
5272                 this.hide();
5273                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5274                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5275                 Roo.log("New Dialog Message:" +  options.msg )
5276                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5277                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5278                 
5279             }
5280             var d = this.getDialog();
5281             opt = options;
5282             d.setTitle(opt.title || "&#160;");
5283             d.closeEl.setDisplayed(opt.closable !== false);
5284             activeTextEl = textboxEl;
5285             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5286             if(opt.prompt){
5287                 if(opt.multiline){
5288                     textboxEl.hide();
5289                     textareaEl.show();
5290                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5291                         opt.multiline : this.defaultTextHeight);
5292                     activeTextEl = textareaEl;
5293                 }else{
5294                     textboxEl.show();
5295                     textareaEl.hide();
5296                 }
5297             }else{
5298                 textboxEl.hide();
5299                 textareaEl.hide();
5300             }
5301             progressEl.setDisplayed(opt.progress === true);
5302             if (opt.progress) {
5303                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5304             }
5305             this.updateProgress(0);
5306             activeTextEl.dom.value = opt.value || "";
5307             if(opt.prompt){
5308                 dlg.setDefaultButton(activeTextEl);
5309             }else{
5310                 var bs = opt.buttons;
5311                 var db = null;
5312                 if(bs && bs.ok){
5313                     db = buttons["ok"];
5314                 }else if(bs && bs.yes){
5315                     db = buttons["yes"];
5316                 }
5317                 dlg.setDefaultButton(db);
5318             }
5319             bwidth = updateButtons(opt.buttons);
5320             this.updateText(opt.msg);
5321             if(opt.cls){
5322                 d.el.addClass(opt.cls);
5323             }
5324             d.proxyDrag = opt.proxyDrag === true;
5325             d.modal = opt.modal !== false;
5326             d.mask = opt.modal !== false ? mask : false;
5327             if(!d.isVisible()){
5328                 // force it to the end of the z-index stack so it gets a cursor in FF
5329                 document.body.appendChild(dlg.el.dom);
5330                 d.animateTarget = null;
5331                 d.show(options.animEl);
5332             }
5333             return this;
5334         },
5335
5336         /**
5337          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5338          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5339          * and closing the message box when the process is complete.
5340          * @param {String} title The title bar text
5341          * @param {String} msg The message box body text
5342          * @return {Roo.MessageBox} This message box
5343          */
5344         progress : function(title, msg){
5345             this.show({
5346                 title : title,
5347                 msg : msg,
5348                 buttons: false,
5349                 progress:true,
5350                 closable:false,
5351                 minWidth: this.minProgressWidth,
5352                 modal : true
5353             });
5354             return this;
5355         },
5356
5357         /**
5358          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5359          * If a callback function is passed it will be called after the user clicks the button, and the
5360          * id of the button that was clicked will be passed as the only parameter to the callback
5361          * (could also be the top-right close button).
5362          * @param {String} title The title bar text
5363          * @param {String} msg The message box body text
5364          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5365          * @param {Object} scope (optional) The scope of the callback function
5366          * @return {Roo.MessageBox} This message box
5367          */
5368         alert : function(title, msg, fn, scope)
5369         {
5370             this.show({
5371                 title : title,
5372                 msg : msg,
5373                 buttons: this.OK,
5374                 fn: fn,
5375                 closable : false,
5376                 scope : scope,
5377                 modal : true
5378             });
5379             return this;
5380         },
5381
5382         /**
5383          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5384          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5385          * You are responsible for closing the message box when the process is complete.
5386          * @param {String} msg The message box body text
5387          * @param {String} title (optional) The title bar text
5388          * @return {Roo.MessageBox} This message box
5389          */
5390         wait : function(msg, title){
5391             this.show({
5392                 title : title,
5393                 msg : msg,
5394                 buttons: false,
5395                 closable:false,
5396                 progress:true,
5397                 modal:true,
5398                 width:300,
5399                 wait:true
5400             });
5401             waitTimer = Roo.TaskMgr.start({
5402                 run: function(i){
5403                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5404                 },
5405                 interval: 1000
5406             });
5407             return this;
5408         },
5409
5410         /**
5411          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5412          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5413          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5414          * @param {String} title The title bar text
5415          * @param {String} msg The message box body text
5416          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5417          * @param {Object} scope (optional) The scope of the callback function
5418          * @return {Roo.MessageBox} This message box
5419          */
5420         confirm : function(title, msg, fn, scope){
5421             this.show({
5422                 title : title,
5423                 msg : msg,
5424                 buttons: this.YESNO,
5425                 fn: fn,
5426                 scope : scope,
5427                 modal : true
5428             });
5429             return this;
5430         },
5431
5432         /**
5433          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5434          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5435          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5436          * (could also be the top-right close button) and the text that was entered will be passed as the two
5437          * parameters to the callback.
5438          * @param {String} title The title bar text
5439          * @param {String} msg The message box body text
5440          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5441          * @param {Object} scope (optional) The scope of the callback function
5442          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5443          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5444          * @return {Roo.MessageBox} This message box
5445          */
5446         prompt : function(title, msg, fn, scope, multiline){
5447             this.show({
5448                 title : title,
5449                 msg : msg,
5450                 buttons: this.OKCANCEL,
5451                 fn: fn,
5452                 minWidth:250,
5453                 scope : scope,
5454                 prompt:true,
5455                 multiline: multiline,
5456                 modal : true
5457             });
5458             return this;
5459         },
5460
5461         /**
5462          * Button config that displays a single OK button
5463          * @type Object
5464          */
5465         OK : {ok:true},
5466         /**
5467          * Button config that displays Yes and No buttons
5468          * @type Object
5469          */
5470         YESNO : {yes:true, no:true},
5471         /**
5472          * Button config that displays OK and Cancel buttons
5473          * @type Object
5474          */
5475         OKCANCEL : {ok:true, cancel:true},
5476         /**
5477          * Button config that displays Yes, No and Cancel buttons
5478          * @type Object
5479          */
5480         YESNOCANCEL : {yes:true, no:true, cancel:true},
5481
5482         /**
5483          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5484          * @type Number
5485          */
5486         defaultTextHeight : 75,
5487         /**
5488          * The maximum width in pixels of the message box (defaults to 600)
5489          * @type Number
5490          */
5491         maxWidth : 600,
5492         /**
5493          * The minimum width in pixels of the message box (defaults to 100)
5494          * @type Number
5495          */
5496         minWidth : 100,
5497         /**
5498          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5499          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5500          * @type Number
5501          */
5502         minProgressWidth : 250,
5503         /**
5504          * An object containing the default button text strings that can be overriden for localized language support.
5505          * Supported properties are: ok, cancel, yes and no.
5506          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5507          * @type Object
5508          */
5509         buttonText : {
5510             ok : "OK",
5511             cancel : "Cancel",
5512             yes : "Yes",
5513             no : "No"
5514         }
5515     };
5516 }();
5517
5518 /**
5519  * Shorthand for {@link Roo.MessageBox}
5520  */
5521 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5522 Roo.Msg = Roo.Msg || Roo.MessageBox;
5523 /*
5524  * - LGPL
5525  *
5526  * navbar
5527  * 
5528  */
5529
5530 /**
5531  * @class Roo.bootstrap.Navbar
5532  * @extends Roo.bootstrap.Component
5533  * Bootstrap Navbar class
5534
5535  * @constructor
5536  * Create a new Navbar
5537  * @param {Object} config The config object
5538  */
5539
5540
5541 Roo.bootstrap.Navbar = function(config){
5542     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5543     this.addEvents({
5544         // raw events
5545         /**
5546          * @event beforetoggle
5547          * Fire before toggle the menu
5548          * @param {Roo.EventObject} e
5549          */
5550         "beforetoggle" : true
5551     });
5552 };
5553
5554 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5555     
5556     
5557    
5558     // private
5559     navItems : false,
5560     loadMask : false,
5561     
5562     
5563     getAutoCreate : function(){
5564         
5565         
5566         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5567         
5568     },
5569     
5570     initEvents :function ()
5571     {
5572         //Roo.log(this.el.select('.navbar-toggle',true));
5573         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5574         
5575         var mark = {
5576             tag: "div",
5577             cls:"x-dlg-mask"
5578         };
5579         
5580         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5581         
5582         var size = this.el.getSize();
5583         this.maskEl.setSize(size.width, size.height);
5584         this.maskEl.enableDisplayMode("block");
5585         this.maskEl.hide();
5586         
5587         if(this.loadMask){
5588             this.maskEl.show();
5589         }
5590     },
5591     
5592     
5593     getChildContainer : function()
5594     {
5595         if (this.el && this.el.select('.collapse').getCount()) {
5596             return this.el.select('.collapse',true).first();
5597         }
5598         
5599         return this.el;
5600     },
5601     
5602     mask : function()
5603     {
5604         this.maskEl.show();
5605     },
5606     
5607     unmask : function()
5608     {
5609         this.maskEl.hide();
5610     },
5611     onToggle : function()
5612     {
5613         
5614         if(this.fireEvent('beforetoggle', this) === false){
5615             return;
5616         }
5617         var ce = this.el.select('.navbar-collapse',true).first();
5618       
5619         if (!ce.hasClass('show')) {
5620            this.expand();
5621         } else {
5622             this.collapse();
5623         }
5624         
5625         
5626     
5627     },
5628     /**
5629      * Expand the navbar pulldown 
5630      */
5631     expand : function ()
5632     {
5633        
5634         var ce = this.el.select('.navbar-collapse',true).first();
5635         if (ce.hasClass('collapsing')) {
5636             return;
5637         }
5638         ce.dom.style.height = '';
5639                // show it...
5640         ce.addClass('in'); // old...
5641         ce.removeClass('collapse');
5642         ce.addClass('show');
5643         var h = ce.getHeight();
5644         Roo.log(h);
5645         ce.removeClass('show');
5646         // at this point we should be able to see it..
5647         ce.addClass('collapsing');
5648         
5649         ce.setHeight(0); // resize it ...
5650         ce.on('transitionend', function() {
5651             //Roo.log('done transition');
5652             ce.removeClass('collapsing');
5653             ce.addClass('show');
5654             ce.removeClass('collapse');
5655
5656             ce.dom.style.height = '';
5657         }, this, { single: true} );
5658         ce.setHeight(h);
5659         ce.dom.scrollTop = 0;
5660     },
5661     /**
5662      * Collapse the navbar pulldown 
5663      */
5664     collapse : function()
5665     {
5666          var ce = this.el.select('.navbar-collapse',true).first();
5667        
5668         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5669             // it's collapsed or collapsing..
5670             return;
5671         }
5672         ce.removeClass('in'); // old...
5673         ce.setHeight(ce.getHeight());
5674         ce.removeClass('show');
5675         ce.addClass('collapsing');
5676         
5677         ce.on('transitionend', function() {
5678             ce.dom.style.height = '';
5679             ce.removeClass('collapsing');
5680             ce.addClass('collapse');
5681         }, this, { single: true} );
5682         ce.setHeight(0);
5683     }
5684     
5685     
5686     
5687 });
5688
5689
5690
5691  
5692
5693  /*
5694  * - LGPL
5695  *
5696  * navbar
5697  * 
5698  */
5699
5700 /**
5701  * @class Roo.bootstrap.NavSimplebar
5702  * @extends Roo.bootstrap.Navbar
5703  * Bootstrap Sidebar class
5704  *
5705  * @cfg {Boolean} inverse is inverted color
5706  * 
5707  * @cfg {String} type (nav | pills | tabs)
5708  * @cfg {Boolean} arrangement stacked | justified
5709  * @cfg {String} align (left | right) alignment
5710  * 
5711  * @cfg {Boolean} main (true|false) main nav bar? default false
5712  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5713  * 
5714  * @cfg {String} tag (header|footer|nav|div) default is nav 
5715
5716  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5717  * 
5718  * 
5719  * @constructor
5720  * Create a new Sidebar
5721  * @param {Object} config The config object
5722  */
5723
5724
5725 Roo.bootstrap.NavSimplebar = function(config){
5726     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5727 };
5728
5729 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5730     
5731     inverse: false,
5732     
5733     type: false,
5734     arrangement: '',
5735     align : false,
5736     
5737     weight : 'light',
5738     
5739     main : false,
5740     
5741     
5742     tag : false,
5743     
5744     
5745     getAutoCreate : function(){
5746         
5747         
5748         var cfg = {
5749             tag : this.tag || 'div',
5750             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5751         };
5752         if (['light','white'].indexOf(this.weight) > -1) {
5753             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5754         }
5755         cfg.cls += ' bg-' + this.weight;
5756         
5757         if (this.inverse) {
5758             cfg.cls += ' navbar-inverse';
5759             
5760         }
5761         
5762         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5763         
5764         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5765             return cfg;
5766         }
5767         
5768         
5769     
5770         
5771         cfg.cn = [
5772             {
5773                 cls: 'nav nav-' + this.xtype,
5774                 tag : 'ul'
5775             }
5776         ];
5777         
5778          
5779         this.type = this.type || 'nav';
5780         if (['tabs','pills'].indexOf(this.type) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.type
5782         
5783         
5784         } else {
5785             if (this.type!=='nav') {
5786                 Roo.log('nav type must be nav/tabs/pills')
5787             }
5788             cfg.cn[0].cls += ' navbar-nav'
5789         }
5790         
5791         
5792         
5793         
5794         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5795             cfg.cn[0].cls += ' nav-' + this.arrangement;
5796         }
5797         
5798         
5799         if (this.align === 'right') {
5800             cfg.cn[0].cls += ' navbar-right';
5801         }
5802         
5803         
5804         
5805         
5806         return cfg;
5807     
5808         
5809     }
5810     
5811     
5812     
5813 });
5814
5815
5816
5817  
5818
5819  
5820        /*
5821  * - LGPL
5822  *
5823  * navbar
5824  * navbar-fixed-top
5825  * navbar-expand-md  fixed-top 
5826  */
5827
5828 /**
5829  * @class Roo.bootstrap.NavHeaderbar
5830  * @extends Roo.bootstrap.NavSimplebar
5831  * Bootstrap Sidebar class
5832  *
5833  * @cfg {String} brand what is brand
5834  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5835  * @cfg {String} brand_href href of the brand
5836  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5837  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5838  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5839  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5840  * 
5841  * @constructor
5842  * Create a new Sidebar
5843  * @param {Object} config The config object
5844  */
5845
5846
5847 Roo.bootstrap.NavHeaderbar = function(config){
5848     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5849       
5850 };
5851
5852 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5853     
5854     position: '',
5855     brand: '',
5856     brand_href: false,
5857     srButton : true,
5858     autohide : false,
5859     desktopCenter : false,
5860    
5861     
5862     getAutoCreate : function(){
5863         
5864         var   cfg = {
5865             tag: this.nav || 'nav',
5866             cls: 'navbar navbar-expand-md',
5867             role: 'navigation',
5868             cn: []
5869         };
5870         
5871         var cn = cfg.cn;
5872         if (this.desktopCenter) {
5873             cn.push({cls : 'container', cn : []});
5874             cn = cn[0].cn;
5875         }
5876         
5877         if(this.srButton){
5878             var btn = {
5879                 tag: 'button',
5880                 type: 'button',
5881                 cls: 'navbar-toggle navbar-toggler',
5882                 'data-toggle': 'collapse',
5883                 cn: [
5884                     {
5885                         tag: 'span',
5886                         cls: 'sr-only',
5887                         html: 'Toggle navigation'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar navbar-toggler-icon'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     },
5897                     {
5898                         tag: 'span',
5899                         cls: 'icon-bar'
5900                     }
5901                 ]
5902             };
5903             
5904             cn.push( Roo.bootstrap.version == 4 ? btn : {
5905                 tag: 'div',
5906                 cls: 'navbar-header',
5907                 cn: [
5908                     btn
5909                 ]
5910             });
5911         }
5912         
5913         cn.push({
5914             tag: 'div',
5915             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5916             cn : []
5917         });
5918         
5919         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5920         
5921         if (['light','white'].indexOf(this.weight) > -1) {
5922             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5923         }
5924         cfg.cls += ' bg-' + this.weight;
5925         
5926         
5927         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5928             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5929             
5930             // tag can override this..
5931             
5932             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5933         }
5934         
5935         if (this.brand !== '') {
5936             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5937             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5938                 tag: 'a',
5939                 href: this.brand_href ? this.brand_href : '#',
5940                 cls: 'navbar-brand',
5941                 cn: [
5942                 this.brand
5943                 ]
5944             });
5945         }
5946         
5947         if(this.main){
5948             cfg.cls += ' main-nav';
5949         }
5950         
5951         
5952         return cfg;
5953
5954         
5955     },
5956     getHeaderChildContainer : function()
5957     {
5958         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5959             return this.el.select('.navbar-header',true).first();
5960         }
5961         
5962         return this.getChildContainer();
5963     },
5964     
5965     getChildContainer : function()
5966     {
5967          
5968         return this.el.select('.roo-navbar-collapse',true).first();
5969          
5970         
5971     },
5972     
5973     initEvents : function()
5974     {
5975         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5976         
5977         if (this.autohide) {
5978             
5979             var prevScroll = 0;
5980             var ft = this.el;
5981             
5982             Roo.get(document).on('scroll',function(e) {
5983                 var ns = Roo.get(document).getScroll().top;
5984                 var os = prevScroll;
5985                 prevScroll = ns;
5986                 
5987                 if(ns > os){
5988                     ft.removeClass('slideDown');
5989                     ft.addClass('slideUp');
5990                     return;
5991                 }
5992                 ft.removeClass('slideUp');
5993                 ft.addClass('slideDown');
5994                  
5995               
5996           },this);
5997         }
5998     }    
5999     
6000 });
6001
6002
6003
6004  
6005
6006  /*
6007  * - LGPL
6008  *
6009  * navbar
6010  * 
6011  */
6012
6013 /**
6014  * @class Roo.bootstrap.NavSidebar
6015  * @extends Roo.bootstrap.Navbar
6016  * Bootstrap Sidebar class
6017  * 
6018  * @constructor
6019  * Create a new Sidebar
6020  * @param {Object} config The config object
6021  */
6022
6023
6024 Roo.bootstrap.NavSidebar = function(config){
6025     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6026 };
6027
6028 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6029     
6030     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6031     
6032     getAutoCreate : function(){
6033         
6034         
6035         return  {
6036             tag: 'div',
6037             cls: 'sidebar sidebar-nav'
6038         };
6039     
6040         
6041     }
6042     
6043     
6044     
6045 });
6046
6047
6048
6049  
6050
6051  /*
6052  * - LGPL
6053  *
6054  * nav group
6055  * 
6056  */
6057
6058 /**
6059  * @class Roo.bootstrap.NavGroup
6060  * @extends Roo.bootstrap.Component
6061  * Bootstrap NavGroup class
6062  * @cfg {String} align (left|right)
6063  * @cfg {Boolean} inverse
6064  * @cfg {String} type (nav|pills|tab) default nav
6065  * @cfg {String} navId - reference Id for navbar.
6066  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6067  * 
6068  * @constructor
6069  * Create a new nav group
6070  * @param {Object} config The config object
6071  */
6072
6073 Roo.bootstrap.NavGroup = function(config){
6074     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6075     this.navItems = [];
6076    
6077     Roo.bootstrap.NavGroup.register(this);
6078      this.addEvents({
6079         /**
6080              * @event changed
6081              * Fires when the active item changes
6082              * @param {Roo.bootstrap.NavGroup} this
6083              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6084              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6085          */
6086         'changed': true
6087      });
6088     
6089 };
6090
6091 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6092     
6093     align: '',
6094     inverse: false,
6095     form: false,
6096     type: 'nav',
6097     navId : '',
6098     // private
6099     pilltype : true,
6100     
6101     navItems : false, 
6102     
6103     getAutoCreate : function()
6104     {
6105         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6106         
6107         cfg = {
6108             tag : 'ul',
6109             cls: 'nav' 
6110         };
6111         if (Roo.bootstrap.version == 4) {
6112             if (['tabs','pills'].indexOf(this.type) != -1) {
6113                 cfg.cls += ' nav-' + this.type; 
6114             } else {
6115                 // trying to remove so header bar can right align top?
6116                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6117                     // do not use on header bar... 
6118                     cfg.cls += ' navbar-nav';
6119                 }
6120             }
6121             
6122         } else {
6123             if (['tabs','pills'].indexOf(this.type) != -1) {
6124                 cfg.cls += ' nav-' + this.type
6125             } else {
6126                 if (this.type !== 'nav') {
6127                     Roo.log('nav type must be nav/tabs/pills')
6128                 }
6129                 cfg.cls += ' navbar-nav'
6130             }
6131         }
6132         
6133         if (this.parent() && this.parent().sidebar) {
6134             cfg = {
6135                 tag: 'ul',
6136                 cls: 'dashboard-menu sidebar-menu'
6137             };
6138             
6139             return cfg;
6140         }
6141         
6142         if (this.form === true) {
6143             cfg = {
6144                 tag: 'form',
6145                 cls: 'navbar-form form-inline'
6146             };
6147             //nav navbar-right ml-md-auto
6148             if (this.align === 'right') {
6149                 cfg.cls += ' navbar-right ml-md-auto';
6150             } else {
6151                 cfg.cls += ' navbar-left';
6152             }
6153         }
6154         
6155         if (this.align === 'right') {
6156             cfg.cls += ' navbar-right ml-md-auto';
6157         } else {
6158             cfg.cls += ' mr-auto';
6159         }
6160         
6161         if (this.inverse) {
6162             cfg.cls += ' navbar-inverse';
6163             
6164         }
6165         
6166         
6167         return cfg;
6168     },
6169     /**
6170     * sets the active Navigation item
6171     * @param {Roo.bootstrap.NavItem} the new current navitem
6172     */
6173     setActiveItem : function(item)
6174     {
6175         var prev = false;
6176         Roo.each(this.navItems, function(v){
6177             if (v == item) {
6178                 return ;
6179             }
6180             if (v.isActive()) {
6181                 v.setActive(false, true);
6182                 prev = v;
6183                 
6184             }
6185             
6186         });
6187
6188         item.setActive(true, true);
6189         this.fireEvent('changed', this, item, prev);
6190         
6191         
6192     },
6193     /**
6194     * gets the active Navigation item
6195     * @return {Roo.bootstrap.NavItem} the current navitem
6196     */
6197     getActive : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v){
6202             
6203             if (v.isActive()) {
6204                 prev = v;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     
6212     indexOfNav : function()
6213     {
6214         
6215         var prev = false;
6216         Roo.each(this.navItems, function(v,i){
6217             
6218             if (v.isActive()) {
6219                 prev = i;
6220                 
6221             }
6222             
6223         });
6224         return prev;
6225     },
6226     /**
6227     * adds a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     addItem : function(cfg)
6231     {
6232         if (this.form && Roo.bootstrap.version == 4) {
6233             cfg.tag = 'div';
6234         }
6235         var cn = new Roo.bootstrap.NavItem(cfg);
6236         this.register(cn);
6237         cn.parentId = this.id;
6238         cn.onRender(this.el, null);
6239         return cn;
6240     },
6241     /**
6242     * register a Navigation item
6243     * @param {Roo.bootstrap.NavItem} the navitem to add
6244     */
6245     register : function(item)
6246     {
6247         this.navItems.push( item);
6248         item.navId = this.navId;
6249     
6250     },
6251     
6252     /**
6253     * clear all the Navigation item
6254     */
6255    
6256     clearAll : function()
6257     {
6258         this.navItems = [];
6259         this.el.dom.innerHTML = '';
6260     },
6261     
6262     getNavItem: function(tabId)
6263     {
6264         var ret = false;
6265         Roo.each(this.navItems, function(e) {
6266             if (e.tabId == tabId) {
6267                ret =  e;
6268                return false;
6269             }
6270             return true;
6271             
6272         });
6273         return ret;
6274     },
6275     
6276     setActiveNext : function()
6277     {
6278         var i = this.indexOfNav(this.getActive());
6279         if (i > this.navItems.length) {
6280             return;
6281         }
6282         this.setActiveItem(this.navItems[i+1]);
6283     },
6284     setActivePrev : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i  < 1) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i-1]);
6291     },
6292     clearWasActive : function(except) {
6293         Roo.each(this.navItems, function(e) {
6294             if (e.tabId != except.tabId && e.was_active) {
6295                e.was_active = false;
6296                return false;
6297             }
6298             return true;
6299             
6300         });
6301     },
6302     getWasActive : function ()
6303     {
6304         var r = false;
6305         Roo.each(this.navItems, function(e) {
6306             if (e.was_active) {
6307                r = e;
6308                return false;
6309             }
6310             return true;
6311             
6312         });
6313         return r;
6314     }
6315     
6316     
6317 });
6318
6319  
6320 Roo.apply(Roo.bootstrap.NavGroup, {
6321     
6322     groups: {},
6323      /**
6324     * register a Navigation Group
6325     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6326     */
6327     register : function(navgrp)
6328     {
6329         this.groups[navgrp.navId] = navgrp;
6330         
6331     },
6332     /**
6333     * fetch a Navigation Group based on the navigation ID
6334     * @param {string} the navgroup to add
6335     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6336     */
6337     get: function(navId) {
6338         if (typeof(this.groups[navId]) == 'undefined') {
6339             return false;
6340             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6341         }
6342         return this.groups[navId] ;
6343     }
6344     
6345     
6346     
6347 });
6348
6349  /*
6350  * - LGPL
6351  *
6352  * row
6353  * 
6354  */
6355
6356 /**
6357  * @class Roo.bootstrap.NavItem
6358  * @extends Roo.bootstrap.Component
6359  * Bootstrap Navbar.NavItem class
6360  * @cfg {String} href  link to
6361  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6362  * @cfg {Boolean} button_outline show and outlined button
6363  * @cfg {String} html content of button
6364  * @cfg {String} badge text inside badge
6365  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6366  * @cfg {String} glyphicon DEPRICATED - use fa
6367  * @cfg {String} icon DEPRICATED - use fa
6368  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6369  * @cfg {Boolean} active Is item active
6370  * @cfg {Boolean} disabled Is item disabled
6371  * @cfg {String} linkcls  Link Class
6372  * @cfg {Boolean} preventDefault (true | false) default false
6373  * @cfg {String} tabId the tab that this item activates.
6374  * @cfg {String} tagtype (a|span) render as a href or span?
6375  * @cfg {Boolean} animateRef (true|false) link to element default false  
6376   
6377  * @constructor
6378  * Create a new Navbar Item
6379  * @param {Object} config The config object
6380  */
6381 Roo.bootstrap.NavItem = function(config){
6382     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6383     this.addEvents({
6384         // raw events
6385         /**
6386          * @event click
6387          * The raw click event for the entire grid.
6388          * @param {Roo.EventObject} e
6389          */
6390         "click" : true,
6391          /**
6392             * @event changed
6393             * Fires when the active item active state changes
6394             * @param {Roo.bootstrap.NavItem} this
6395             * @param {boolean} state the new state
6396              
6397          */
6398         'changed': true,
6399         /**
6400             * @event scrollto
6401             * Fires when scroll to element
6402             * @param {Roo.bootstrap.NavItem} this
6403             * @param {Object} options
6404             * @param {Roo.EventObject} e
6405              
6406          */
6407         'scrollto': true
6408     });
6409    
6410 };
6411
6412 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6413     
6414     href: false,
6415     html: '',
6416     badge: '',
6417     icon: false,
6418     fa : false,
6419     glyphicon: false,
6420     active: false,
6421     preventDefault : false,
6422     tabId : false,
6423     tagtype : 'a',
6424     tag: 'li',
6425     disabled : false,
6426     animateRef : false,
6427     was_active : false,
6428     button_weight : '',
6429     button_outline : false,
6430     linkcls : '',
6431     navLink: false,
6432     
6433     getAutoCreate : function(){
6434          
6435         var cfg = {
6436             tag: this.tag,
6437             cls: 'nav-item'
6438         };
6439         
6440         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6441         
6442         if (this.active) {
6443             cfg.cls +=  ' active' ;
6444         }
6445         if (this.disabled) {
6446             cfg.cls += ' disabled';
6447         }
6448         
6449         // BS4 only?
6450         if (this.button_weight.length) {
6451             cfg.tag = this.href ? 'a' : 'button';
6452             cfg.html = this.html || '';
6453             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6454             if (this.href) {
6455                 cfg.href = this.href;
6456             }
6457             if (this.fa) {
6458                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6459             } else {
6460                 cfg.cls += " nav-html";
6461             }
6462             
6463             // menu .. should add dropdown-menu class - so no need for carat..
6464             
6465             if (this.badge !== '') {
6466                  
6467                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6468             }
6469             return cfg;
6470         }
6471         
6472         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6473             cfg.cn = [
6474                 {
6475                     tag: this.tagtype,
6476                     href : this.href || "#",
6477                     html: this.html || '',
6478                     cls : ''
6479                 }
6480             ];
6481             if (this.tagtype == 'a') {
6482                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6483         
6484             }
6485             if (this.icon) {
6486                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6487             } else  if (this.fa) {
6488                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6489             } else if(this.glyphicon) {
6490                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6491             } else {
6492                 cfg.cn[0].cls += " nav-html";
6493             }
6494             
6495             if (this.menu) {
6496                 cfg.cn[0].html += " <span class='caret'></span>";
6497              
6498             }
6499             
6500             if (this.badge !== '') {
6501                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6502             }
6503         }
6504         
6505         
6506         
6507         return cfg;
6508     },
6509     onRender : function(ct, position)
6510     {
6511        // Roo.log("Call onRender: " + this.xtype);
6512         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6513             this.tag = 'div';
6514         }
6515         
6516         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6517         this.navLink = this.el.select('.nav-link',true).first();
6518         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6519         return ret;
6520     },
6521       
6522     
6523     initEvents: function() 
6524     {
6525         if (typeof (this.menu) != 'undefined') {
6526             this.menu.parentType = this.xtype;
6527             this.menu.triggerEl = this.el;
6528             this.menu = this.addxtype(Roo.apply({}, this.menu));
6529         }
6530         
6531         this.el.on('click', this.onClick, this);
6532         
6533         //if(this.tagtype == 'span'){
6534         //    this.el.select('span',true).on('click', this.onClick, this);
6535         //}
6536        
6537         // at this point parent should be available..
6538         this.parent().register(this);
6539     },
6540     
6541     onClick : function(e)
6542     {
6543         if (e.getTarget('.dropdown-menu-item')) {
6544             // did you click on a menu itemm.... - then don't trigger onclick..
6545             return;
6546         }
6547         
6548         if(
6549                 this.preventDefault || 
6550                 this.href == '#' 
6551         ){
6552             Roo.log("NavItem - prevent Default?");
6553             e.preventDefault();
6554         }
6555         
6556         if (this.disabled) {
6557             return;
6558         }
6559         
6560         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6561         if (tg && tg.transition) {
6562             Roo.log("waiting for the transitionend");
6563             return;
6564         }
6565         
6566         
6567         
6568         //Roo.log("fire event clicked");
6569         if(this.fireEvent('click', this, e) === false){
6570             return;
6571         };
6572         
6573         if(this.tagtype == 'span'){
6574             return;
6575         }
6576         
6577         //Roo.log(this.href);
6578         var ael = this.el.select('a',true).first();
6579         //Roo.log(ael);
6580         
6581         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6582             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6583             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6584                 return; // ignore... - it's a 'hash' to another page.
6585             }
6586             Roo.log("NavItem - prevent Default?");
6587             e.preventDefault();
6588             this.scrollToElement(e);
6589         }
6590         
6591         
6592         var p =  this.parent();
6593    
6594         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6595             if (typeof(p.setActiveItem) !== 'undefined') {
6596                 p.setActiveItem(this);
6597             }
6598         }
6599         
6600         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6601         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6602             // remove the collapsed menu expand...
6603             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6604         }
6605     },
6606     
6607     isActive: function () {
6608         return this.active
6609     },
6610     setActive : function(state, fire, is_was_active)
6611     {
6612         if (this.active && !state && this.navId) {
6613             this.was_active = true;
6614             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6615             if (nv) {
6616                 nv.clearWasActive(this);
6617             }
6618             
6619         }
6620         this.active = state;
6621         
6622         if (!state ) {
6623             this.el.removeClass('active');
6624             this.navLink ? this.navLink.removeClass('active') : false;
6625         } else if (!this.el.hasClass('active')) {
6626             
6627             this.el.addClass('active');
6628             if (Roo.bootstrap.version == 4 && this.navLink ) {
6629                 this.navLink.addClass('active');
6630             }
6631             
6632         }
6633         if (fire) {
6634             this.fireEvent('changed', this, state);
6635         }
6636         
6637         // show a panel if it's registered and related..
6638         
6639         if (!this.navId || !this.tabId || !state || is_was_active) {
6640             return;
6641         }
6642         
6643         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6644         if (!tg) {
6645             return;
6646         }
6647         var pan = tg.getPanelByName(this.tabId);
6648         if (!pan) {
6649             return;
6650         }
6651         // if we can not flip to new panel - go back to old nav highlight..
6652         if (false == tg.showPanel(pan)) {
6653             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6654             if (nv) {
6655                 var onav = nv.getWasActive();
6656                 if (onav) {
6657                     onav.setActive(true, false, true);
6658                 }
6659             }
6660             
6661         }
6662         
6663         
6664         
6665     },
6666      // this should not be here...
6667     setDisabled : function(state)
6668     {
6669         this.disabled = state;
6670         if (!state ) {
6671             this.el.removeClass('disabled');
6672         } else if (!this.el.hasClass('disabled')) {
6673             this.el.addClass('disabled');
6674         }
6675         
6676     },
6677     
6678     /**
6679      * Fetch the element to display the tooltip on.
6680      * @return {Roo.Element} defaults to this.el
6681      */
6682     tooltipEl : function()
6683     {
6684         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6685     },
6686     
6687     scrollToElement : function(e)
6688     {
6689         var c = document.body;
6690         
6691         /*
6692          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6693          */
6694         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6695             c = document.documentElement;
6696         }
6697         
6698         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6699         
6700         if(!target){
6701             return;
6702         }
6703
6704         var o = target.calcOffsetsTo(c);
6705         
6706         var options = {
6707             target : target,
6708             value : o[1]
6709         };
6710         
6711         this.fireEvent('scrollto', this, options, e);
6712         
6713         Roo.get(c).scrollTo('top', options.value, true);
6714         
6715         return;
6716     },
6717     /**
6718      * Set the HTML (text content) of the item
6719      * @param {string} html  content for the nav item
6720      */
6721     setHtml : function(html)
6722     {
6723         this.html = html;
6724         this.htmlEl.dom.innerHTML = html;
6725         
6726     } 
6727 });
6728  
6729
6730  /*
6731  * - LGPL
6732  *
6733  * sidebar item
6734  *
6735  *  li
6736  *    <span> icon </span>
6737  *    <span> text </span>
6738  *    <span>badge </span>
6739  */
6740
6741 /**
6742  * @class Roo.bootstrap.NavSidebarItem
6743  * @extends Roo.bootstrap.NavItem
6744  * Bootstrap Navbar.NavSidebarItem class
6745  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6746  * {Boolean} open is the menu open
6747  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6748  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6749  * {String} buttonSize (sm|md|lg)the extra classes for the button
6750  * {Boolean} showArrow show arrow next to the text (default true)
6751  * @constructor
6752  * Create a new Navbar Button
6753  * @param {Object} config The config object
6754  */
6755 Roo.bootstrap.NavSidebarItem = function(config){
6756     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6757     this.addEvents({
6758         // raw events
6759         /**
6760          * @event click
6761          * The raw click event for the entire grid.
6762          * @param {Roo.EventObject} e
6763          */
6764         "click" : true,
6765          /**
6766             * @event changed
6767             * Fires when the active item active state changes
6768             * @param {Roo.bootstrap.NavSidebarItem} this
6769             * @param {boolean} state the new state
6770              
6771          */
6772         'changed': true
6773     });
6774    
6775 };
6776
6777 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6778     
6779     badgeWeight : 'default',
6780     
6781     open: false,
6782     
6783     buttonView : false,
6784     
6785     buttonWeight : 'default',
6786     
6787     buttonSize : 'md',
6788     
6789     showArrow : true,
6790     
6791     getAutoCreate : function(){
6792         
6793         
6794         var a = {
6795                 tag: 'a',
6796                 href : this.href || '#',
6797                 cls: '',
6798                 html : '',
6799                 cn : []
6800         };
6801         
6802         if(this.buttonView){
6803             a = {
6804                 tag: 'button',
6805                 href : this.href || '#',
6806                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6807                 html : this.html,
6808                 cn : []
6809             };
6810         }
6811         
6812         var cfg = {
6813             tag: 'li',
6814             cls: '',
6815             cn: [ a ]
6816         };
6817         
6818         if (this.active) {
6819             cfg.cls += ' active';
6820         }
6821         
6822         if (this.disabled) {
6823             cfg.cls += ' disabled';
6824         }
6825         if (this.open) {
6826             cfg.cls += ' open x-open';
6827         }
6828         // left icon..
6829         if (this.glyphicon || this.icon) {
6830             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6831             a.cn.push({ tag : 'i', cls : c }) ;
6832         }
6833         
6834         if(!this.buttonView){
6835             var span = {
6836                 tag: 'span',
6837                 html : this.html || ''
6838             };
6839
6840             a.cn.push(span);
6841             
6842         }
6843         
6844         if (this.badge !== '') {
6845             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6846         }
6847         
6848         if (this.menu) {
6849             
6850             if(this.showArrow){
6851                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6852             }
6853             
6854             a.cls += ' dropdown-toggle treeview' ;
6855         }
6856         
6857         return cfg;
6858     },
6859     
6860     initEvents : function()
6861     { 
6862         if (typeof (this.menu) != 'undefined') {
6863             this.menu.parentType = this.xtype;
6864             this.menu.triggerEl = this.el;
6865             this.menu = this.addxtype(Roo.apply({}, this.menu));
6866         }
6867         
6868         this.el.on('click', this.onClick, this);
6869         
6870         if(this.badge !== ''){
6871             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6872         }
6873         
6874     },
6875     
6876     onClick : function(e)
6877     {
6878         if(this.disabled){
6879             e.preventDefault();
6880             return;
6881         }
6882         
6883         if(this.preventDefault){
6884             e.preventDefault();
6885         }
6886         
6887         this.fireEvent('click', this, e);
6888     },
6889     
6890     disable : function()
6891     {
6892         this.setDisabled(true);
6893     },
6894     
6895     enable : function()
6896     {
6897         this.setDisabled(false);
6898     },
6899     
6900     setDisabled : function(state)
6901     {
6902         if(this.disabled == state){
6903             return;
6904         }
6905         
6906         this.disabled = state;
6907         
6908         if (state) {
6909             this.el.addClass('disabled');
6910             return;
6911         }
6912         
6913         this.el.removeClass('disabled');
6914         
6915         return;
6916     },
6917     
6918     setActive : function(state)
6919     {
6920         if(this.active == state){
6921             return;
6922         }
6923         
6924         this.active = state;
6925         
6926         if (state) {
6927             this.el.addClass('active');
6928             return;
6929         }
6930         
6931         this.el.removeClass('active');
6932         
6933         return;
6934     },
6935     
6936     isActive: function () 
6937     {
6938         return this.active;
6939     },
6940     
6941     setBadge : function(str)
6942     {
6943         if(!this.badgeEl){
6944             return;
6945         }
6946         
6947         this.badgeEl.dom.innerHTML = str;
6948     }
6949     
6950    
6951      
6952  
6953 });
6954  
6955
6956  /*
6957  * - LGPL
6958  *
6959  *  Breadcrumb Nav
6960  * 
6961  */
6962 Roo.namespace('Roo.bootstrap.breadcrumb');
6963
6964
6965 /**
6966  * @class Roo.bootstrap.breadcrumb.Nav
6967  * @extends Roo.bootstrap.Component
6968  * Bootstrap Breadcrumb Nav Class
6969  *  
6970  * @children Roo.bootstrap.breadcrumb.Item
6971  * 
6972  * @constructor
6973  * Create a new breadcrumb.Nav
6974  * @param {Object} config The config object
6975  */
6976
6977
6978 Roo.bootstrap.breadcrumb.Nav = function(config){
6979     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6980     
6981     
6982 };
6983
6984 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6985     
6986     getAutoCreate : function()
6987     {
6988
6989         var cfg = {
6990             tag: 'nav',
6991             cn : [
6992                 {
6993                     tag : 'ol',
6994                     cls : 'breadcrumb'
6995                 }
6996             ]
6997             
6998         };
6999           
7000         return cfg;
7001     },
7002     
7003     initEvents: function()
7004     {
7005         this.olEl = this.el.select('ol',true).first();    
7006     },
7007     getChildContainer : function()
7008     {
7009         return this.olEl;  
7010     }
7011     
7012 });
7013
7014  /*
7015  * - LGPL
7016  *
7017  *  Breadcrumb Item
7018  * 
7019  */
7020
7021
7022 /**
7023  * @class Roo.bootstrap.breadcrumb.Nav
7024  * @extends Roo.bootstrap.Component
7025  * Bootstrap Breadcrumb Nav Class
7026  *  
7027  * @children Roo.bootstrap.breadcrumb.Component
7028  * @cfg {String} html the content of the link.
7029  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7030  * @cfg {Boolean} active is it active
7031
7032  * 
7033  * @constructor
7034  * Create a new breadcrumb.Nav
7035  * @param {Object} config The config object
7036  */
7037
7038 Roo.bootstrap.breadcrumb.Item = function(config){
7039     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7040     this.addEvents({
7041         // img events
7042         /**
7043          * @event click
7044          * The img click event for the img.
7045          * @param {Roo.EventObject} e
7046          */
7047         "click" : true
7048     });
7049     
7050 };
7051
7052 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7053     
7054     href: false,
7055     html : '',
7056     
7057     getAutoCreate : function()
7058     {
7059
7060         var cfg = {
7061             tag: 'li',
7062             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7063         };
7064         if (this.href !== false) {
7065             cfg.cn = [{
7066                 tag : 'a',
7067                 href : this.href,
7068                 html : this.html
7069             }];
7070         } else {
7071             cfg.html = this.html;
7072         }
7073         
7074         return cfg;
7075     },
7076     
7077     initEvents: function()
7078     {
7079         if (this.href) {
7080             this.el.select('a', true).first().on('click',this.onClick, this)
7081         }
7082         
7083     },
7084     onClick : function(e)
7085     {
7086         e.preventDefault();
7087         this.fireEvent('click',this,  e);
7088     }
7089     
7090 });
7091
7092  /*
7093  * - LGPL
7094  *
7095  * row
7096  * 
7097  */
7098
7099 /**
7100  * @class Roo.bootstrap.Row
7101  * @extends Roo.bootstrap.Component
7102  * @children Roo.bootstrap.Component
7103  * Bootstrap Row class (contains columns...)
7104  * 
7105  * @constructor
7106  * Create a new Row
7107  * @param {Object} config The config object
7108  */
7109
7110 Roo.bootstrap.Row = function(config){
7111     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7112 };
7113
7114 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7115     
7116     getAutoCreate : function(){
7117        return {
7118             cls: 'row clearfix'
7119        };
7120     }
7121     
7122     
7123 });
7124
7125  
7126
7127  /*
7128  * - LGPL
7129  *
7130  * pagination
7131  * 
7132  */
7133
7134 /**
7135  * @class Roo.bootstrap.Pagination
7136  * @extends Roo.bootstrap.Component
7137  * Bootstrap Pagination class
7138  * @cfg {String} size xs | sm | md | lg
7139  * @cfg {Boolean} inverse false | true
7140  * 
7141  * @constructor
7142  * Create a new Pagination
7143  * @param {Object} config The config object
7144  */
7145
7146 Roo.bootstrap.Pagination = function(config){
7147     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7148 };
7149
7150 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7151     
7152     cls: false,
7153     size: false,
7154     inverse: false,
7155     
7156     getAutoCreate : function(){
7157         var cfg = {
7158             tag: 'ul',
7159                 cls: 'pagination'
7160         };
7161         if (this.inverse) {
7162             cfg.cls += ' inverse';
7163         }
7164         if (this.html) {
7165             cfg.html=this.html;
7166         }
7167         if (this.cls) {
7168             cfg.cls += " " + this.cls;
7169         }
7170         return cfg;
7171     }
7172    
7173 });
7174
7175  
7176
7177  /*
7178  * - LGPL
7179  *
7180  * Pagination item
7181  * 
7182  */
7183
7184
7185 /**
7186  * @class Roo.bootstrap.PaginationItem
7187  * @extends Roo.bootstrap.Component
7188  * Bootstrap PaginationItem class
7189  * @cfg {String} html text
7190  * @cfg {String} href the link
7191  * @cfg {Boolean} preventDefault (true | false) default true
7192  * @cfg {Boolean} active (true | false) default false
7193  * @cfg {Boolean} disabled default false
7194  * 
7195  * 
7196  * @constructor
7197  * Create a new PaginationItem
7198  * @param {Object} config The config object
7199  */
7200
7201
7202 Roo.bootstrap.PaginationItem = function(config){
7203     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7204     this.addEvents({
7205         // raw events
7206         /**
7207          * @event click
7208          * The raw click event for the entire grid.
7209          * @param {Roo.EventObject} e
7210          */
7211         "click" : true
7212     });
7213 };
7214
7215 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7216     
7217     href : false,
7218     html : false,
7219     preventDefault: true,
7220     active : false,
7221     cls : false,
7222     disabled: false,
7223     
7224     getAutoCreate : function(){
7225         var cfg= {
7226             tag: 'li',
7227             cn: [
7228                 {
7229                     tag : 'a',
7230                     href : this.href ? this.href : '#',
7231                     html : this.html ? this.html : ''
7232                 }
7233             ]
7234         };
7235         
7236         if(this.cls){
7237             cfg.cls = this.cls;
7238         }
7239         
7240         if(this.disabled){
7241             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7242         }
7243         
7244         if(this.active){
7245             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7246         }
7247         
7248         return cfg;
7249     },
7250     
7251     initEvents: function() {
7252         
7253         this.el.on('click', this.onClick, this);
7254         
7255     },
7256     onClick : function(e)
7257     {
7258         Roo.log('PaginationItem on click ');
7259         if(this.preventDefault){
7260             e.preventDefault();
7261         }
7262         
7263         if(this.disabled){
7264             return;
7265         }
7266         
7267         this.fireEvent('click', this, e);
7268     }
7269    
7270 });
7271
7272  
7273
7274  /*
7275  * - LGPL
7276  *
7277  * slider
7278  * 
7279  */
7280
7281
7282 /**
7283  * @class Roo.bootstrap.Slider
7284  * @extends Roo.bootstrap.Component
7285  * Bootstrap Slider class
7286  *    
7287  * @constructor
7288  * Create a new Slider
7289  * @param {Object} config The config object
7290  */
7291
7292 Roo.bootstrap.Slider = function(config){
7293     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7294 };
7295
7296 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7297     
7298     getAutoCreate : function(){
7299         
7300         var cfg = {
7301             tag: 'div',
7302             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7303             cn: [
7304                 {
7305                     tag: 'a',
7306                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7307                 }
7308             ]
7309         };
7310         
7311         return cfg;
7312     }
7313    
7314 });
7315
7316  /*
7317  * Based on:
7318  * Ext JS Library 1.1.1
7319  * Copyright(c) 2006-2007, Ext JS, LLC.
7320  *
7321  * Originally Released Under LGPL - original licence link has changed is not relivant.
7322  *
7323  * Fork - LGPL
7324  * <script type="text/javascript">
7325  */
7326  /**
7327  * @extends Roo.dd.DDProxy
7328  * @class Roo.grid.SplitDragZone
7329  * Support for Column Header resizing
7330  * @constructor
7331  * @param {Object} config
7332  */
7333 // private
7334 // This is a support class used internally by the Grid components
7335 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7336     this.grid = grid;
7337     this.view = grid.getView();
7338     this.proxy = this.view.resizeProxy;
7339     Roo.grid.SplitDragZone.superclass.constructor.call(
7340         this,
7341         hd, // ID
7342         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7343         {  // CONFIG
7344             dragElId : Roo.id(this.proxy.dom),
7345             resizeFrame:false
7346         }
7347     );
7348     
7349     this.setHandleElId(Roo.id(hd));
7350     if (hd2 !== false) {
7351         this.setOuterHandleElId(Roo.id(hd2));
7352     }
7353     
7354     this.scroll = false;
7355 };
7356 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7357     fly: Roo.Element.fly,
7358
7359     b4StartDrag : function(x, y){
7360         this.view.headersDisabled = true;
7361         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7362                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7363         );
7364         this.proxy.setHeight(h);
7365         
7366         // for old system colWidth really stored the actual width?
7367         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7368         // which in reality did not work.. - it worked only for fixed sizes
7369         // for resizable we need to use actual sizes.
7370         var w = this.cm.getColumnWidth(this.cellIndex);
7371         if (!this.view.mainWrap) {
7372             // bootstrap.
7373             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7374         }
7375         
7376         
7377         
7378         // this was w-this.grid.minColumnWidth;
7379         // doesnt really make sense? - w = thie curren width or the rendered one?
7380         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7381         this.resetConstraints();
7382         this.setXConstraint(minw, 1000);
7383         this.setYConstraint(0, 0);
7384         this.minX = x - minw;
7385         this.maxX = x + 1000;
7386         this.startPos = x;
7387         if (!this.view.mainWrap) { // this is Bootstrap code..
7388             this.getDragEl().style.display='block';
7389         }
7390         
7391         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7392     },
7393
7394
7395     handleMouseDown : function(e){
7396         ev = Roo.EventObject.setEvent(e);
7397         var t = this.fly(ev.getTarget());
7398         if(t.hasClass("x-grid-split")){
7399             this.cellIndex = this.view.getCellIndex(t.dom);
7400             this.split = t.dom;
7401             this.cm = this.grid.colModel;
7402             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7403                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7404             }
7405         }
7406     },
7407
7408     endDrag : function(e){
7409         this.view.headersDisabled = false;
7410         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7411         var diff = endX - this.startPos;
7412         // 
7413         var w = this.cm.getColumnWidth(this.cellIndex);
7414         if (!this.view.mainWrap) {
7415             w = 0;
7416         }
7417         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7418     },
7419
7420     autoOffset : function(){
7421         this.setDelta(0,0);
7422     }
7423 });/*
7424  * Based on:
7425  * Ext JS Library 1.1.1
7426  * Copyright(c) 2006-2007, Ext JS, LLC.
7427  *
7428  * Originally Released Under LGPL - original licence link has changed is not relivant.
7429  *
7430  * Fork - LGPL
7431  * <script type="text/javascript">
7432  */
7433
7434 /**
7435  * @class Roo.grid.AbstractSelectionModel
7436  * @extends Roo.util.Observable
7437  * @abstract
7438  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7439  * implemented by descendant classes.  This class should not be directly instantiated.
7440  * @constructor
7441  */
7442 Roo.grid.AbstractSelectionModel = function(){
7443     this.locked = false;
7444     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7445 };
7446
7447 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7448     /** @ignore Called by the grid automatically. Do not call directly. */
7449     init : function(grid){
7450         this.grid = grid;
7451         this.initEvents();
7452     },
7453
7454     /**
7455      * Locks the selections.
7456      */
7457     lock : function(){
7458         this.locked = true;
7459     },
7460
7461     /**
7462      * Unlocks the selections.
7463      */
7464     unlock : function(){
7465         this.locked = false;
7466     },
7467
7468     /**
7469      * Returns true if the selections are locked.
7470      * @return {Boolean}
7471      */
7472     isLocked : function(){
7473         return this.locked;
7474     }
7475 });/*
7476  * Based on:
7477  * Ext JS Library 1.1.1
7478  * Copyright(c) 2006-2007, Ext JS, LLC.
7479  *
7480  * Originally Released Under LGPL - original licence link has changed is not relivant.
7481  *
7482  * Fork - LGPL
7483  * <script type="text/javascript">
7484  */
7485 /**
7486  * @extends Roo.grid.AbstractSelectionModel
7487  * @class Roo.grid.RowSelectionModel
7488  * The default SelectionModel used by {@link Roo.grid.Grid}.
7489  * It supports multiple selections and keyboard selection/navigation. 
7490  * @constructor
7491  * @param {Object} config
7492  */
7493 Roo.grid.RowSelectionModel = function(config){
7494     Roo.apply(this, config);
7495     this.selections = new Roo.util.MixedCollection(false, function(o){
7496         return o.id;
7497     });
7498
7499     this.last = false;
7500     this.lastActive = false;
7501
7502     this.addEvents({
7503         /**
7504         * @event selectionchange
7505         * Fires when the selection changes
7506         * @param {SelectionModel} this
7507         */
7508        "selectionchange" : true,
7509        /**
7510         * @event afterselectionchange
7511         * Fires after the selection changes (eg. by key press or clicking)
7512         * @param {SelectionModel} this
7513         */
7514        "afterselectionchange" : true,
7515        /**
7516         * @event beforerowselect
7517         * Fires when a row is selected being selected, return false to cancel.
7518         * @param {SelectionModel} this
7519         * @param {Number} rowIndex The selected index
7520         * @param {Boolean} keepExisting False if other selections will be cleared
7521         */
7522        "beforerowselect" : true,
7523        /**
7524         * @event rowselect
7525         * Fires when a row is selected.
7526         * @param {SelectionModel} this
7527         * @param {Number} rowIndex The selected index
7528         * @param {Roo.data.Record} r The record
7529         */
7530        "rowselect" : true,
7531        /**
7532         * @event rowdeselect
7533         * Fires when a row is deselected.
7534         * @param {SelectionModel} this
7535         * @param {Number} rowIndex The selected index
7536         */
7537         "rowdeselect" : true
7538     });
7539     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7540     this.locked = false;
7541 };
7542
7543 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7544     /**
7545      * @cfg {Boolean} singleSelect
7546      * True to allow selection of only one row at a time (defaults to false)
7547      */
7548     singleSelect : false,
7549
7550     // private
7551     initEvents : function(){
7552
7553         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7554             this.grid.on("mousedown", this.handleMouseDown, this);
7555         }else{ // allow click to work like normal
7556             this.grid.on("rowclick", this.handleDragableRowClick, this);
7557         }
7558         // bootstrap does not have a view..
7559         var view = this.grid.view ? this.grid.view : this.grid;
7560         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7561             "up" : function(e){
7562                 if(!e.shiftKey){
7563                     this.selectPrevious(e.shiftKey);
7564                 }else if(this.last !== false && this.lastActive !== false){
7565                     var last = this.last;
7566                     this.selectRange(this.last,  this.lastActive-1);
7567                     view.focusRow(this.lastActive);
7568                     if(last !== false){
7569                         this.last = last;
7570                     }
7571                 }else{
7572                     this.selectFirstRow();
7573                 }
7574                 this.fireEvent("afterselectionchange", this);
7575             },
7576             "down" : function(e){
7577                 if(!e.shiftKey){
7578                     this.selectNext(e.shiftKey);
7579                 }else if(this.last !== false && this.lastActive !== false){
7580                     var last = this.last;
7581                     this.selectRange(this.last,  this.lastActive+1);
7582                     view.focusRow(this.lastActive);
7583                     if(last !== false){
7584                         this.last = last;
7585                     }
7586                 }else{
7587                     this.selectFirstRow();
7588                 }
7589                 this.fireEvent("afterselectionchange", this);
7590             },
7591             scope: this
7592         });
7593
7594          
7595         view.on("refresh", this.onRefresh, this);
7596         view.on("rowupdated", this.onRowUpdated, this);
7597         view.on("rowremoved", this.onRemove, this);
7598     },
7599
7600     // private
7601     onRefresh : function(){
7602         var ds = this.grid.ds, i, v = this.grid.view;
7603         var s = this.selections;
7604         s.each(function(r){
7605             if((i = ds.indexOfId(r.id)) != -1){
7606                 v.onRowSelect(i);
7607                 s.add(ds.getAt(i)); // updating the selection relate data
7608             }else{
7609                 s.remove(r);
7610             }
7611         });
7612     },
7613
7614     // private
7615     onRemove : function(v, index, r){
7616         this.selections.remove(r);
7617     },
7618
7619     // private
7620     onRowUpdated : function(v, index, r){
7621         if(this.isSelected(r)){
7622             v.onRowSelect(index);
7623         }
7624     },
7625
7626     /**
7627      * Select records.
7628      * @param {Array} records The records to select
7629      * @param {Boolean} keepExisting (optional) True to keep existing selections
7630      */
7631     selectRecords : function(records, keepExisting){
7632         if(!keepExisting){
7633             this.clearSelections();
7634         }
7635         var ds = this.grid.ds;
7636         for(var i = 0, len = records.length; i < len; i++){
7637             this.selectRow(ds.indexOf(records[i]), true);
7638         }
7639     },
7640
7641     /**
7642      * Gets the number of selected rows.
7643      * @return {Number}
7644      */
7645     getCount : function(){
7646         return this.selections.length;
7647     },
7648
7649     /**
7650      * Selects the first row in the grid.
7651      */
7652     selectFirstRow : function(){
7653         this.selectRow(0);
7654     },
7655
7656     /**
7657      * Select the last row.
7658      * @param {Boolean} keepExisting (optional) True to keep existing selections
7659      */
7660     selectLastRow : function(keepExisting){
7661         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7662     },
7663
7664     /**
7665      * Selects the row immediately following the last selected row.
7666      * @param {Boolean} keepExisting (optional) True to keep existing selections
7667      */
7668     selectNext : function(keepExisting){
7669         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7670             this.selectRow(this.last+1, keepExisting);
7671             var view = this.grid.view ? this.grid.view : this.grid;
7672             view.focusRow(this.last);
7673         }
7674     },
7675
7676     /**
7677      * Selects the row that precedes the last selected row.
7678      * @param {Boolean} keepExisting (optional) True to keep existing selections
7679      */
7680     selectPrevious : function(keepExisting){
7681         if(this.last){
7682             this.selectRow(this.last-1, keepExisting);
7683             var view = this.grid.view ? this.grid.view : this.grid;
7684             view.focusRow(this.last);
7685         }
7686     },
7687
7688     /**
7689      * Returns the selected records
7690      * @return {Array} Array of selected records
7691      */
7692     getSelections : function(){
7693         return [].concat(this.selections.items);
7694     },
7695
7696     /**
7697      * Returns the first selected record.
7698      * @return {Record}
7699      */
7700     getSelected : function(){
7701         return this.selections.itemAt(0);
7702     },
7703
7704
7705     /**
7706      * Clears all selections.
7707      */
7708     clearSelections : function(fast){
7709         if(this.locked) {
7710             return;
7711         }
7712         if(fast !== true){
7713             var ds = this.grid.ds;
7714             var s = this.selections;
7715             s.each(function(r){
7716                 this.deselectRow(ds.indexOfId(r.id));
7717             }, this);
7718             s.clear();
7719         }else{
7720             this.selections.clear();
7721         }
7722         this.last = false;
7723     },
7724
7725
7726     /**
7727      * Selects all rows.
7728      */
7729     selectAll : function(){
7730         if(this.locked) {
7731             return;
7732         }
7733         this.selections.clear();
7734         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7735             this.selectRow(i, true);
7736         }
7737     },
7738
7739     /**
7740      * Returns True if there is a selection.
7741      * @return {Boolean}
7742      */
7743     hasSelection : function(){
7744         return this.selections.length > 0;
7745     },
7746
7747     /**
7748      * Returns True if the specified row is selected.
7749      * @param {Number/Record} record The record or index of the record to check
7750      * @return {Boolean}
7751      */
7752     isSelected : function(index){
7753         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7754         return (r && this.selections.key(r.id) ? true : false);
7755     },
7756
7757     /**
7758      * Returns True if the specified record id is selected.
7759      * @param {String} id The id of record to check
7760      * @return {Boolean}
7761      */
7762     isIdSelected : function(id){
7763         return (this.selections.key(id) ? true : false);
7764     },
7765
7766     // private
7767     handleMouseDown : function(e, t)
7768     {
7769         var view = this.grid.view ? this.grid.view : this.grid;
7770         var rowIndex;
7771         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7772             return;
7773         };
7774         if(e.shiftKey && this.last !== false){
7775             var last = this.last;
7776             this.selectRange(last, rowIndex, e.ctrlKey);
7777             this.last = last; // reset the last
7778             view.focusRow(rowIndex);
7779         }else{
7780             var isSelected = this.isSelected(rowIndex);
7781             if(e.button !== 0 && isSelected){
7782                 view.focusRow(rowIndex);
7783             }else if(e.ctrlKey && isSelected){
7784                 this.deselectRow(rowIndex);
7785             }else if(!isSelected){
7786                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7787                 view.focusRow(rowIndex);
7788             }
7789         }
7790         this.fireEvent("afterselectionchange", this);
7791     },
7792     // private
7793     handleDragableRowClick :  function(grid, rowIndex, e) 
7794     {
7795         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7796             this.selectRow(rowIndex, false);
7797             var view = this.grid.view ? this.grid.view : this.grid;
7798             view.focusRow(rowIndex);
7799              this.fireEvent("afterselectionchange", this);
7800         }
7801     },
7802     
7803     /**
7804      * Selects multiple rows.
7805      * @param {Array} rows Array of the indexes of the row to select
7806      * @param {Boolean} keepExisting (optional) True to keep existing selections
7807      */
7808     selectRows : function(rows, keepExisting){
7809         if(!keepExisting){
7810             this.clearSelections();
7811         }
7812         for(var i = 0, len = rows.length; i < len; i++){
7813             this.selectRow(rows[i], true);
7814         }
7815     },
7816
7817     /**
7818      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7819      * @param {Number} startRow The index of the first row in the range
7820      * @param {Number} endRow The index of the last row in the range
7821      * @param {Boolean} keepExisting (optional) True to retain existing selections
7822      */
7823     selectRange : function(startRow, endRow, keepExisting){
7824         if(this.locked) {
7825             return;
7826         }
7827         if(!keepExisting){
7828             this.clearSelections();
7829         }
7830         if(startRow <= endRow){
7831             for(var i = startRow; i <= endRow; i++){
7832                 this.selectRow(i, true);
7833             }
7834         }else{
7835             for(var i = startRow; i >= endRow; i--){
7836                 this.selectRow(i, true);
7837             }
7838         }
7839     },
7840
7841     /**
7842      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7843      * @param {Number} startRow The index of the first row in the range
7844      * @param {Number} endRow The index of the last row in the range
7845      */
7846     deselectRange : function(startRow, endRow, preventViewNotify){
7847         if(this.locked) {
7848             return;
7849         }
7850         for(var i = startRow; i <= endRow; i++){
7851             this.deselectRow(i, preventViewNotify);
7852         }
7853     },
7854
7855     /**
7856      * Selects a row.
7857      * @param {Number} row The index of the row to select
7858      * @param {Boolean} keepExisting (optional) True to keep existing selections
7859      */
7860     selectRow : function(index, keepExisting, preventViewNotify){
7861         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7862             return;
7863         }
7864         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7865             if(!keepExisting || this.singleSelect){
7866                 this.clearSelections();
7867             }
7868             var r = this.grid.ds.getAt(index);
7869             this.selections.add(r);
7870             this.last = this.lastActive = index;
7871             if(!preventViewNotify){
7872                 var view = this.grid.view ? this.grid.view : this.grid;
7873                 view.onRowSelect(index);
7874             }
7875             this.fireEvent("rowselect", this, index, r);
7876             this.fireEvent("selectionchange", this);
7877         }
7878     },
7879
7880     /**
7881      * Deselects a row.
7882      * @param {Number} row The index of the row to deselect
7883      */
7884     deselectRow : function(index, preventViewNotify){
7885         if(this.locked) {
7886             return;
7887         }
7888         if(this.last == index){
7889             this.last = false;
7890         }
7891         if(this.lastActive == index){
7892             this.lastActive = false;
7893         }
7894         var r = this.grid.ds.getAt(index);
7895         this.selections.remove(r);
7896         if(!preventViewNotify){
7897             var view = this.grid.view ? this.grid.view : this.grid;
7898             view.onRowDeselect(index);
7899         }
7900         this.fireEvent("rowdeselect", this, index);
7901         this.fireEvent("selectionchange", this);
7902     },
7903
7904     // private
7905     restoreLast : function(){
7906         if(this._last){
7907             this.last = this._last;
7908         }
7909     },
7910
7911     // private
7912     acceptsNav : function(row, col, cm){
7913         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7914     },
7915
7916     // private
7917     onEditorKey : function(field, e){
7918         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7919         if(k == e.TAB){
7920             e.stopEvent();
7921             ed.completeEdit();
7922             if(e.shiftKey){
7923                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7924             }else{
7925                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7926             }
7927         }else if(k == e.ENTER && !e.ctrlKey){
7928             e.stopEvent();
7929             ed.completeEdit();
7930             if(e.shiftKey){
7931                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7932             }else{
7933                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7934             }
7935         }else if(k == e.ESC){
7936             ed.cancelEdit();
7937         }
7938         if(newCell){
7939             g.startEditing(newCell[0], newCell[1]);
7940         }
7941     }
7942 });/*
7943  * Based on:
7944  * Ext JS Library 1.1.1
7945  * Copyright(c) 2006-2007, Ext JS, LLC.
7946  *
7947  * Originally Released Under LGPL - original licence link has changed is not relivant.
7948  *
7949  * Fork - LGPL
7950  * <script type="text/javascript">
7951  */
7952  
7953
7954 /**
7955  * @class Roo.grid.ColumnModel
7956  * @extends Roo.util.Observable
7957  * This is the default implementation of a ColumnModel used by the Grid. It defines
7958  * the columns in the grid.
7959  * <br>Usage:<br>
7960  <pre><code>
7961  var colModel = new Roo.grid.ColumnModel([
7962         {header: "Ticker", width: 60, sortable: true, locked: true},
7963         {header: "Company Name", width: 150, sortable: true},
7964         {header: "Market Cap.", width: 100, sortable: true},
7965         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7966         {header: "Employees", width: 100, sortable: true, resizable: false}
7967  ]);
7968  </code></pre>
7969  * <p>
7970  
7971  * The config options listed for this class are options which may appear in each
7972  * individual column definition.
7973  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7974  * @constructor
7975  * @param {Object} config An Array of column config objects. See this class's
7976  * config objects for details.
7977 */
7978 Roo.grid.ColumnModel = function(config){
7979         /**
7980      * The config passed into the constructor
7981      */
7982     this.config = []; //config;
7983     this.lookup = {};
7984
7985     // if no id, create one
7986     // if the column does not have a dataIndex mapping,
7987     // map it to the order it is in the config
7988     for(var i = 0, len = config.length; i < len; i++){
7989         this.addColumn(config[i]);
7990         
7991     }
7992
7993     /**
7994      * The width of columns which have no width specified (defaults to 100)
7995      * @type Number
7996      */
7997     this.defaultWidth = 100;
7998
7999     /**
8000      * Default sortable of columns which have no sortable specified (defaults to false)
8001      * @type Boolean
8002      */
8003     this.defaultSortable = false;
8004
8005     this.addEvents({
8006         /**
8007              * @event widthchange
8008              * Fires when the width of a column changes.
8009              * @param {ColumnModel} this
8010              * @param {Number} columnIndex The column index
8011              * @param {Number} newWidth The new width
8012              */
8013             "widthchange": true,
8014         /**
8015              * @event headerchange
8016              * Fires when the text of a header changes.
8017              * @param {ColumnModel} this
8018              * @param {Number} columnIndex The column index
8019              * @param {Number} newText The new header text
8020              */
8021             "headerchange": true,
8022         /**
8023              * @event hiddenchange
8024              * Fires when a column is hidden or "unhidden".
8025              * @param {ColumnModel} this
8026              * @param {Number} columnIndex The column index
8027              * @param {Boolean} hidden true if hidden, false otherwise
8028              */
8029             "hiddenchange": true,
8030             /**
8031          * @event columnmoved
8032          * Fires when a column is moved.
8033          * @param {ColumnModel} this
8034          * @param {Number} oldIndex
8035          * @param {Number} newIndex
8036          */
8037         "columnmoved" : true,
8038         /**
8039          * @event columlockchange
8040          * Fires when a column's locked state is changed
8041          * @param {ColumnModel} this
8042          * @param {Number} colIndex
8043          * @param {Boolean} locked true if locked
8044          */
8045         "columnlockchange" : true
8046     });
8047     Roo.grid.ColumnModel.superclass.constructor.call(this);
8048 };
8049 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8050     /**
8051      * @cfg {String} header The header text to display in the Grid view.
8052      */
8053         /**
8054      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8055      */
8056         /**
8057      * @cfg {String} smHeader Header at Bootsrap Small width
8058      */
8059         /**
8060      * @cfg {String} mdHeader Header at Bootsrap Medium width
8061      */
8062         /**
8063      * @cfg {String} lgHeader Header at Bootsrap Large width
8064      */
8065         /**
8066      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8067      */
8068     /**
8069      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8070      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8071      * specified, the column's index is used as an index into the Record's data Array.
8072      */
8073     /**
8074      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8075      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8076      */
8077     /**
8078      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8079      * Defaults to the value of the {@link #defaultSortable} property.
8080      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8081      */
8082     /**
8083      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8084      */
8085     /**
8086      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8087      */
8088     /**
8089      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8090      */
8091     /**
8092      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8093      */
8094     /**
8095      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8096      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8097      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8098      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8099      */
8100        /**
8101      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8102      */
8103     /**
8104      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8105      */
8106     /**
8107      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8108      */
8109     /**
8110      * @cfg {String} cursor (Optional)
8111      */
8112     /**
8113      * @cfg {String} tooltip (Optional)
8114      */
8115     /**
8116      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8117      */
8118     /**
8119      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8120      */
8121     /**
8122      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8123      */
8124     /**
8125      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8126      */
8127         /**
8128      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8129      */
8130     /**
8131      * Returns the id of the column at the specified index.
8132      * @param {Number} index The column index
8133      * @return {String} the id
8134      */
8135     getColumnId : function(index){
8136         return this.config[index].id;
8137     },
8138
8139     /**
8140      * Returns the column for a specified id.
8141      * @param {String} id The column id
8142      * @return {Object} the column
8143      */
8144     getColumnById : function(id){
8145         return this.lookup[id];
8146     },
8147
8148     
8149     /**
8150      * Returns the column Object for a specified dataIndex.
8151      * @param {String} dataIndex The column dataIndex
8152      * @return {Object|Boolean} the column or false if not found
8153      */
8154     getColumnByDataIndex: function(dataIndex){
8155         var index = this.findColumnIndex(dataIndex);
8156         return index > -1 ? this.config[index] : false;
8157     },
8158     
8159     /**
8160      * Returns the index for a specified column id.
8161      * @param {String} id The column id
8162      * @return {Number} the index, or -1 if not found
8163      */
8164     getIndexById : function(id){
8165         for(var i = 0, len = this.config.length; i < len; i++){
8166             if(this.config[i].id == id){
8167                 return i;
8168             }
8169         }
8170         return -1;
8171     },
8172     
8173     /**
8174      * Returns the index for a specified column dataIndex.
8175      * @param {String} dataIndex The column dataIndex
8176      * @return {Number} the index, or -1 if not found
8177      */
8178     
8179     findColumnIndex : function(dataIndex){
8180         for(var i = 0, len = this.config.length; i < len; i++){
8181             if(this.config[i].dataIndex == dataIndex){
8182                 return i;
8183             }
8184         }
8185         return -1;
8186     },
8187     
8188     
8189     moveColumn : function(oldIndex, newIndex){
8190         var c = this.config[oldIndex];
8191         this.config.splice(oldIndex, 1);
8192         this.config.splice(newIndex, 0, c);
8193         this.dataMap = null;
8194         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8195     },
8196
8197     isLocked : function(colIndex){
8198         return this.config[colIndex].locked === true;
8199     },
8200
8201     setLocked : function(colIndex, value, suppressEvent){
8202         if(this.isLocked(colIndex) == value){
8203             return;
8204         }
8205         this.config[colIndex].locked = value;
8206         if(!suppressEvent){
8207             this.fireEvent("columnlockchange", this, colIndex, value);
8208         }
8209     },
8210
8211     getTotalLockedWidth : function(){
8212         var totalWidth = 0;
8213         for(var i = 0; i < this.config.length; i++){
8214             if(this.isLocked(i) && !this.isHidden(i)){
8215                 this.totalWidth += this.getColumnWidth(i);
8216             }
8217         }
8218         return totalWidth;
8219     },
8220
8221     getLockedCount : function(){
8222         for(var i = 0, len = this.config.length; i < len; i++){
8223             if(!this.isLocked(i)){
8224                 return i;
8225             }
8226         }
8227         
8228         return this.config.length;
8229     },
8230
8231     /**
8232      * Returns the number of columns.
8233      * @return {Number}
8234      */
8235     getColumnCount : function(visibleOnly){
8236         if(visibleOnly === true){
8237             var c = 0;
8238             for(var i = 0, len = this.config.length; i < len; i++){
8239                 if(!this.isHidden(i)){
8240                     c++;
8241                 }
8242             }
8243             return c;
8244         }
8245         return this.config.length;
8246     },
8247
8248     /**
8249      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8250      * @param {Function} fn
8251      * @param {Object} scope (optional)
8252      * @return {Array} result
8253      */
8254     getColumnsBy : function(fn, scope){
8255         var r = [];
8256         for(var i = 0, len = this.config.length; i < len; i++){
8257             var c = this.config[i];
8258             if(fn.call(scope||this, c, i) === true){
8259                 r[r.length] = c;
8260             }
8261         }
8262         return r;
8263     },
8264
8265     /**
8266      * Returns true if the specified column is sortable.
8267      * @param {Number} col The column index
8268      * @return {Boolean}
8269      */
8270     isSortable : function(col){
8271         if(typeof this.config[col].sortable == "undefined"){
8272             return this.defaultSortable;
8273         }
8274         return this.config[col].sortable;
8275     },
8276
8277     /**
8278      * Returns the rendering (formatting) function defined for the column.
8279      * @param {Number} col The column index.
8280      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8281      */
8282     getRenderer : function(col){
8283         if(!this.config[col].renderer){
8284             return Roo.grid.ColumnModel.defaultRenderer;
8285         }
8286         return this.config[col].renderer;
8287     },
8288
8289     /**
8290      * Sets the rendering (formatting) function for a column.
8291      * @param {Number} col The column index
8292      * @param {Function} fn The function to use to process the cell's raw data
8293      * to return HTML markup for the grid view. The render function is called with
8294      * the following parameters:<ul>
8295      * <li>Data value.</li>
8296      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8297      * <li>css A CSS style string to apply to the table cell.</li>
8298      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8299      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8300      * <li>Row index</li>
8301      * <li>Column index</li>
8302      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8303      */
8304     setRenderer : function(col, fn){
8305         this.config[col].renderer = fn;
8306     },
8307
8308     /**
8309      * Returns the width for the specified column.
8310      * @param {Number} col The column index
8311      * @param (optional) {String} gridSize bootstrap width size.
8312      * @return {Number}
8313      */
8314     getColumnWidth : function(col, gridSize)
8315         {
8316                 var cfg = this.config[col];
8317                 
8318                 if (typeof(gridSize) == 'undefined') {
8319                         return cfg.width * 1 || this.defaultWidth;
8320                 }
8321                 if (gridSize === false) { // if we set it..
8322                         return cfg.width || false;
8323                 }
8324                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8325                 
8326                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8327                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8328                                 continue;
8329                         }
8330                         return cfg[ sizes[i] ];
8331                 }
8332                 return 1;
8333                 
8334     },
8335
8336     /**
8337      * Sets the width for a column.
8338      * @param {Number} col The column index
8339      * @param {Number} width The new width
8340      */
8341     setColumnWidth : function(col, width, suppressEvent){
8342         this.config[col].width = width;
8343         this.totalWidth = null;
8344         if(!suppressEvent){
8345              this.fireEvent("widthchange", this, col, width);
8346         }
8347     },
8348
8349     /**
8350      * Returns the total width of all columns.
8351      * @param {Boolean} includeHidden True to include hidden column widths
8352      * @return {Number}
8353      */
8354     getTotalWidth : function(includeHidden){
8355         if(!this.totalWidth){
8356             this.totalWidth = 0;
8357             for(var i = 0, len = this.config.length; i < len; i++){
8358                 if(includeHidden || !this.isHidden(i)){
8359                     this.totalWidth += this.getColumnWidth(i);
8360                 }
8361             }
8362         }
8363         return this.totalWidth;
8364     },
8365
8366     /**
8367      * Returns the header for the specified column.
8368      * @param {Number} col The column index
8369      * @return {String}
8370      */
8371     getColumnHeader : function(col){
8372         return this.config[col].header;
8373     },
8374
8375     /**
8376      * Sets the header for a column.
8377      * @param {Number} col The column index
8378      * @param {String} header The new header
8379      */
8380     setColumnHeader : function(col, header){
8381         this.config[col].header = header;
8382         this.fireEvent("headerchange", this, col, header);
8383     },
8384
8385     /**
8386      * Returns the tooltip for the specified column.
8387      * @param {Number} col The column index
8388      * @return {String}
8389      */
8390     getColumnTooltip : function(col){
8391             return this.config[col].tooltip;
8392     },
8393     /**
8394      * Sets the tooltip for a column.
8395      * @param {Number} col The column index
8396      * @param {String} tooltip The new tooltip
8397      */
8398     setColumnTooltip : function(col, tooltip){
8399             this.config[col].tooltip = tooltip;
8400     },
8401
8402     /**
8403      * Returns the dataIndex for the specified column.
8404      * @param {Number} col The column index
8405      * @return {Number}
8406      */
8407     getDataIndex : function(col){
8408         return this.config[col].dataIndex;
8409     },
8410
8411     /**
8412      * Sets the dataIndex for a column.
8413      * @param {Number} col The column index
8414      * @param {Number} dataIndex The new dataIndex
8415      */
8416     setDataIndex : function(col, dataIndex){
8417         this.config[col].dataIndex = dataIndex;
8418     },
8419
8420     
8421     
8422     /**
8423      * Returns true if the cell is editable.
8424      * @param {Number} colIndex The column index
8425      * @param {Number} rowIndex The row index - this is nto actually used..?
8426      * @return {Boolean}
8427      */
8428     isCellEditable : function(colIndex, rowIndex){
8429         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8430     },
8431
8432     /**
8433      * Returns the editor defined for the cell/column.
8434      * return false or null to disable editing.
8435      * @param {Number} colIndex The column index
8436      * @param {Number} rowIndex The row index
8437      * @return {Object}
8438      */
8439     getCellEditor : function(colIndex, rowIndex){
8440         return this.config[colIndex].editor;
8441     },
8442
8443     /**
8444      * Sets if a column is editable.
8445      * @param {Number} col The column index
8446      * @param {Boolean} editable True if the column is editable
8447      */
8448     setEditable : function(col, editable){
8449         this.config[col].editable = editable;
8450     },
8451
8452
8453     /**
8454      * Returns true if the column is hidden.
8455      * @param {Number} colIndex The column index
8456      * @return {Boolean}
8457      */
8458     isHidden : function(colIndex){
8459         return this.config[colIndex].hidden;
8460     },
8461
8462
8463     /**
8464      * Returns true if the column width cannot be changed
8465      */
8466     isFixed : function(colIndex){
8467         return this.config[colIndex].fixed;
8468     },
8469
8470     /**
8471      * Returns true if the column can be resized
8472      * @return {Boolean}
8473      */
8474     isResizable : function(colIndex){
8475         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8476     },
8477     /**
8478      * Sets if a column is hidden.
8479      * @param {Number} colIndex The column index
8480      * @param {Boolean} hidden True if the column is hidden
8481      */
8482     setHidden : function(colIndex, hidden){
8483         this.config[colIndex].hidden = hidden;
8484         this.totalWidth = null;
8485         this.fireEvent("hiddenchange", this, colIndex, hidden);
8486     },
8487
8488     /**
8489      * Sets the editor for a column.
8490      * @param {Number} col The column index
8491      * @param {Object} editor The editor object
8492      */
8493     setEditor : function(col, editor){
8494         this.config[col].editor = editor;
8495     },
8496     /**
8497      * Add a column (experimental...) - defaults to adding to the end..
8498      * @param {Object} config 
8499     */
8500     addColumn : function(c)
8501     {
8502     
8503         var i = this.config.length;
8504         this.config[i] = c;
8505         
8506         if(typeof c.dataIndex == "undefined"){
8507             c.dataIndex = i;
8508         }
8509         if(typeof c.renderer == "string"){
8510             c.renderer = Roo.util.Format[c.renderer];
8511         }
8512         if(typeof c.id == "undefined"){
8513             c.id = Roo.id();
8514         }
8515         if(c.editor && c.editor.xtype){
8516             c.editor  = Roo.factory(c.editor, Roo.grid);
8517         }
8518         if(c.editor && c.editor.isFormField){
8519             c.editor = new Roo.grid.GridEditor(c.editor);
8520         }
8521         this.lookup[c.id] = c;
8522     }
8523     
8524 });
8525
8526 Roo.grid.ColumnModel.defaultRenderer = function(value)
8527 {
8528     if(typeof value == "object") {
8529         return value;
8530     }
8531         if(typeof value == "string" && value.length < 1){
8532             return "&#160;";
8533         }
8534     
8535         return String.format("{0}", value);
8536 };
8537
8538 // Alias for backwards compatibility
8539 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8540 /*
8541  * Based on:
8542  * Ext JS Library 1.1.1
8543  * Copyright(c) 2006-2007, Ext JS, LLC.
8544  *
8545  * Originally Released Under LGPL - original licence link has changed is not relivant.
8546  *
8547  * Fork - LGPL
8548  * <script type="text/javascript">
8549  */
8550  
8551 /**
8552  * @class Roo.LoadMask
8553  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8554  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8555  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8556  * element's UpdateManager load indicator and will be destroyed after the initial load.
8557  * @constructor
8558  * Create a new LoadMask
8559  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8560  * @param {Object} config The config object
8561  */
8562 Roo.LoadMask = function(el, config){
8563     this.el = Roo.get(el);
8564     Roo.apply(this, config);
8565     if(this.store){
8566         this.store.on('beforeload', this.onBeforeLoad, this);
8567         this.store.on('load', this.onLoad, this);
8568         this.store.on('loadexception', this.onLoadException, this);
8569         this.removeMask = false;
8570     }else{
8571         var um = this.el.getUpdateManager();
8572         um.showLoadIndicator = false; // disable the default indicator
8573         um.on('beforeupdate', this.onBeforeLoad, this);
8574         um.on('update', this.onLoad, this);
8575         um.on('failure', this.onLoad, this);
8576         this.removeMask = true;
8577     }
8578 };
8579
8580 Roo.LoadMask.prototype = {
8581     /**
8582      * @cfg {Boolean} removeMask
8583      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8584      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8585      */
8586     removeMask : false,
8587     /**
8588      * @cfg {String} msg
8589      * The text to display in a centered loading message box (defaults to 'Loading...')
8590      */
8591     msg : 'Loading...',
8592     /**
8593      * @cfg {String} msgCls
8594      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8595      */
8596     msgCls : 'x-mask-loading',
8597
8598     /**
8599      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8600      * @type Boolean
8601      */
8602     disabled: false,
8603
8604     /**
8605      * Disables the mask to prevent it from being displayed
8606      */
8607     disable : function(){
8608        this.disabled = true;
8609     },
8610
8611     /**
8612      * Enables the mask so that it can be displayed
8613      */
8614     enable : function(){
8615         this.disabled = false;
8616     },
8617     
8618     onLoadException : function()
8619     {
8620         Roo.log(arguments);
8621         
8622         if (typeof(arguments[3]) != 'undefined') {
8623             Roo.MessageBox.alert("Error loading",arguments[3]);
8624         } 
8625         /*
8626         try {
8627             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8628                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8629             }   
8630         } catch(e) {
8631             
8632         }
8633         */
8634     
8635         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8636     },
8637     // private
8638     onLoad : function()
8639     {
8640         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8641     },
8642
8643     // private
8644     onBeforeLoad : function(){
8645         if(!this.disabled){
8646             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8647         }
8648     },
8649
8650     // private
8651     destroy : function(){
8652         if(this.store){
8653             this.store.un('beforeload', this.onBeforeLoad, this);
8654             this.store.un('load', this.onLoad, this);
8655             this.store.un('loadexception', this.onLoadException, this);
8656         }else{
8657             var um = this.el.getUpdateManager();
8658             um.un('beforeupdate', this.onBeforeLoad, this);
8659             um.un('update', this.onLoad, this);
8660             um.un('failure', this.onLoad, this);
8661         }
8662     }
8663 };/**
8664  * @class Roo.bootstrap.Table
8665  * @licence LGBL
8666  * @extends Roo.bootstrap.Component
8667  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8668  * Similar to Roo.grid.Grid
8669  * <pre><code>
8670  var table = Roo.factory({
8671     xtype : 'Table',
8672     xns : Roo.bootstrap,
8673     autoSizeColumns: true,
8674     
8675     
8676     store : {
8677         xtype : 'Store',
8678         xns : Roo.data,
8679         remoteSort : true,
8680         sortInfo : { direction : 'ASC', field: 'name' },
8681         proxy : {
8682            xtype : 'HttpProxy',
8683            xns : Roo.data,
8684            method : 'GET',
8685            url : 'https://example.com/some.data.url.json'
8686         },
8687         reader : {
8688            xtype : 'JsonReader',
8689            xns : Roo.data,
8690            fields : [ 'id', 'name', whatever' ],
8691            id : 'id',
8692            root : 'data'
8693         }
8694     },
8695     cm : [
8696         {
8697             xtype : 'ColumnModel',
8698             xns : Roo.grid,
8699             align : 'center',
8700             cursor : 'pointer',
8701             dataIndex : 'is_in_group',
8702             header : "Name",
8703             sortable : true,
8704             renderer : function(v, x , r) {  
8705             
8706                 return String.format("{0}", v)
8707             }
8708             width : 3
8709         } // more columns..
8710     ],
8711     selModel : {
8712         xtype : 'RowSelectionModel',
8713         xns : Roo.bootstrap.Table
8714         // you can add listeners to catch selection change here....
8715     }
8716      
8717
8718  });
8719  // set any options
8720  grid.render(Roo.get("some-div"));
8721 </code></pre>
8722
8723 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8724
8725
8726
8727  *
8728  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8729  * @cfg {Roo.data.Store} store The data store to use
8730  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8731  * 
8732  * @cfg {String} cls table class
8733  *
8734  * 
8735  * @cfg {boolean} striped Should the rows be alternative striped
8736  * @cfg {boolean} bordered Add borders to the table
8737  * @cfg {boolean} hover Add hover highlighting
8738  * @cfg {boolean} condensed Format condensed
8739  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8740  *                also adds table-responsive (see bootstrap docs for details)
8741  * @cfg {Boolean} loadMask (true|false) default false
8742  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8743  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8744  * @cfg {Boolean} rowSelection (true|false) default false
8745  * @cfg {Boolean} cellSelection (true|false) default false
8746  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8747  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8748  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8749  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8750  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8751  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8752  * 
8753  * @constructor
8754  * Create a new Table
8755  * @param {Object} config The config object
8756  */
8757
8758 Roo.bootstrap.Table = function(config)
8759 {
8760     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8761      
8762     // BC...
8763     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8764     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8765     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8766     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8767     
8768     this.view = this; // compat with grid.
8769     
8770     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8771     if (this.sm) {
8772         this.sm.grid = this;
8773         this.selModel = Roo.factory(this.sm, Roo.grid);
8774         this.sm = this.selModel;
8775         this.sm.xmodule = this.xmodule || false;
8776     }
8777     
8778     if (this.cm && typeof(this.cm.config) == 'undefined') {
8779         this.colModel = new Roo.grid.ColumnModel(this.cm);
8780         this.cm = this.colModel;
8781         this.cm.xmodule = this.xmodule || false;
8782     }
8783     if (this.store) {
8784         this.store= Roo.factory(this.store, Roo.data);
8785         this.ds = this.store;
8786         this.ds.xmodule = this.xmodule || false;
8787          
8788     }
8789     if (this.footer && this.store) {
8790         this.footer.dataSource = this.ds;
8791         this.footer = Roo.factory(this.footer);
8792     }
8793     
8794     /** @private */
8795     this.addEvents({
8796         /**
8797          * @event cellclick
8798          * Fires when a cell is clicked
8799          * @param {Roo.bootstrap.Table} this
8800          * @param {Roo.Element} el
8801          * @param {Number} rowIndex
8802          * @param {Number} columnIndex
8803          * @param {Roo.EventObject} e
8804          */
8805         "cellclick" : true,
8806         /**
8807          * @event celldblclick
8808          * Fires when a cell is double clicked
8809          * @param {Roo.bootstrap.Table} this
8810          * @param {Roo.Element} el
8811          * @param {Number} rowIndex
8812          * @param {Number} columnIndex
8813          * @param {Roo.EventObject} e
8814          */
8815         "celldblclick" : true,
8816         /**
8817          * @event rowclick
8818          * Fires when a row is clicked
8819          * @param {Roo.bootstrap.Table} this
8820          * @param {Roo.Element} el
8821          * @param {Number} rowIndex
8822          * @param {Roo.EventObject} e
8823          */
8824         "rowclick" : true,
8825         /**
8826          * @event rowdblclick
8827          * Fires when a row is double clicked
8828          * @param {Roo.bootstrap.Table} this
8829          * @param {Roo.Element} el
8830          * @param {Number} rowIndex
8831          * @param {Roo.EventObject} e
8832          */
8833         "rowdblclick" : true,
8834         /**
8835          * @event mouseover
8836          * Fires when a mouseover occur
8837          * @param {Roo.bootstrap.Table} this
8838          * @param {Roo.Element} el
8839          * @param {Number} rowIndex
8840          * @param {Number} columnIndex
8841          * @param {Roo.EventObject} e
8842          */
8843         "mouseover" : true,
8844         /**
8845          * @event mouseout
8846          * Fires when a mouseout occur
8847          * @param {Roo.bootstrap.Table} this
8848          * @param {Roo.Element} el
8849          * @param {Number} rowIndex
8850          * @param {Number} columnIndex
8851          * @param {Roo.EventObject} e
8852          */
8853         "mouseout" : true,
8854         /**
8855          * @event rowclass
8856          * Fires when a row is rendered, so you can change add a style to it.
8857          * @param {Roo.bootstrap.Table} this
8858          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8859          */
8860         'rowclass' : true,
8861           /**
8862          * @event rowsrendered
8863          * Fires when all the  rows have been rendered
8864          * @param {Roo.bootstrap.Table} this
8865          */
8866         'rowsrendered' : true,
8867         /**
8868          * @event contextmenu
8869          * The raw contextmenu event for the entire grid.
8870          * @param {Roo.EventObject} e
8871          */
8872         "contextmenu" : true,
8873         /**
8874          * @event rowcontextmenu
8875          * Fires when a row is right clicked
8876          * @param {Roo.bootstrap.Table} this
8877          * @param {Number} rowIndex
8878          * @param {Roo.EventObject} e
8879          */
8880         "rowcontextmenu" : true,
8881         /**
8882          * @event cellcontextmenu
8883          * Fires when a cell is right clicked
8884          * @param {Roo.bootstrap.Table} this
8885          * @param {Number} rowIndex
8886          * @param {Number} cellIndex
8887          * @param {Roo.EventObject} e
8888          */
8889          "cellcontextmenu" : true,
8890          /**
8891          * @event headercontextmenu
8892          * Fires when a header is right clicked
8893          * @param {Roo.bootstrap.Table} this
8894          * @param {Number} columnIndex
8895          * @param {Roo.EventObject} e
8896          */
8897         "headercontextmenu" : true,
8898         /**
8899          * @event mousedown
8900          * The raw mousedown event for the entire grid.
8901          * @param {Roo.EventObject} e
8902          */
8903         "mousedown" : true
8904         
8905     });
8906 };
8907
8908 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8909     
8910     cls: false,
8911     
8912     striped : false,
8913     scrollBody : false,
8914     bordered: false,
8915     hover:  false,
8916     condensed : false,
8917     responsive : false,
8918     sm : false,
8919     cm : false,
8920     store : false,
8921     loadMask : false,
8922     footerShow : true,
8923     headerShow : true,
8924     enableColumnResize: true,
8925   
8926     rowSelection : false,
8927     cellSelection : false,
8928     layout : false,
8929
8930     minColumnWidth : 50,
8931     
8932     // Roo.Element - the tbody
8933     bodyEl: false,  // <tbody> Roo.Element - thead element    
8934     headEl: false,  // <thead> Roo.Element - thead element
8935     resizeProxy : false, // proxy element for dragging?
8936
8937
8938     
8939     container: false, // used by gridpanel...
8940     
8941     lazyLoad : false,
8942     
8943     CSS : Roo.util.CSS,
8944     
8945     auto_hide_footer : false,
8946     
8947     view: false, // actually points to this..
8948     
8949     getAutoCreate : function()
8950     {
8951         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8952         
8953         cfg = {
8954             tag: 'table',
8955             cls : 'table', 
8956             cn : []
8957         };
8958         // this get's auto added by panel.Grid
8959         if (this.scrollBody) {
8960             cfg.cls += ' table-body-fixed';
8961         }    
8962         if (this.striped) {
8963             cfg.cls += ' table-striped';
8964         }
8965         
8966         if (this.hover) {
8967             cfg.cls += ' table-hover';
8968         }
8969         if (this.bordered) {
8970             cfg.cls += ' table-bordered';
8971         }
8972         if (this.condensed) {
8973             cfg.cls += ' table-condensed';
8974         }
8975         
8976         if (this.responsive) {
8977             cfg.cls += ' table-responsive';
8978         }
8979         
8980         if (this.cls) {
8981             cfg.cls+=  ' ' +this.cls;
8982         }
8983         
8984         
8985         
8986         if (this.layout) {
8987             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8988         }
8989         
8990         if(this.store || this.cm){
8991             if(this.headerShow){
8992                 cfg.cn.push(this.renderHeader());
8993             }
8994             
8995             cfg.cn.push(this.renderBody());
8996             
8997             if(this.footerShow){
8998                 cfg.cn.push(this.renderFooter());
8999             }
9000             // where does this come from?
9001             //cfg.cls+=  ' TableGrid';
9002         }
9003         
9004         return { cn : [ cfg ] };
9005     },
9006     
9007     initEvents : function()
9008     {   
9009         if(!this.store || !this.cm){
9010             return;
9011         }
9012         if (this.selModel) {
9013             this.selModel.initEvents();
9014         }
9015         
9016         
9017         //Roo.log('initEvents with ds!!!!');
9018         
9019         this.bodyEl = this.el.select('tbody', true).first();
9020         this.headEl = this.el.select('thead', true).first();
9021         this.mainFoot = this.el.select('tfoot', true).first();
9022         
9023         
9024         
9025         
9026         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9027             e.on('click', this.sort, this);
9028         }, this);
9029         
9030         
9031         // why is this done????? = it breaks dialogs??
9032         //this.parent().el.setStyle('position', 'relative');
9033         
9034         
9035         if (this.footer) {
9036             this.footer.parentId = this.id;
9037             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9038             
9039             if(this.lazyLoad){
9040                 this.el.select('tfoot tr td').first().addClass('hide');
9041             }
9042         } 
9043         
9044         if(this.loadMask) {
9045             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9046         }
9047         
9048         this.store.on('load', this.onLoad, this);
9049         this.store.on('beforeload', this.onBeforeLoad, this);
9050         this.store.on('update', this.onUpdate, this);
9051         this.store.on('add', this.onAdd, this);
9052         this.store.on("clear", this.clear, this);
9053         
9054         this.el.on("contextmenu", this.onContextMenu, this);
9055         
9056         
9057         this.cm.on("headerchange", this.onHeaderChange, this);
9058         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9059
9060  //?? does bodyEl get replaced on render?
9061         this.bodyEl.on("click", this.onClick, this);
9062         this.bodyEl.on("dblclick", this.onDblClick, this);        
9063         this.bodyEl.on('scroll', this.onBodyScroll, this);
9064
9065         // guessing mainbody will work - this relays usually caught by selmodel at present.
9066         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9067   
9068   
9069         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9070         
9071   
9072         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9073             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9074         }
9075         
9076         this.initCSS();
9077     },
9078     // Compatibility with grid - we implement all the view features at present.
9079     getView : function()
9080     {
9081         return this;
9082     },
9083     
9084     initCSS : function()
9085     {
9086         
9087         
9088         var cm = this.cm, styles = [];
9089         this.CSS.removeStyleSheet(this.id + '-cssrules');
9090         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9091         // we can honour xs/sm/md/xl  as widths...
9092         // we first have to decide what widht we are currently at...
9093         var sz = Roo.getGridSize();
9094         
9095         var total = 0;
9096         var last = -1;
9097         var cols = []; // visable cols.
9098         var total_abs = 0;
9099         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9100             var w = cm.getColumnWidth(i, false);
9101             if(cm.isHidden(i)){
9102                 cols.push( { rel : false, abs : 0 });
9103                 continue;
9104             }
9105             if (w !== false) {
9106                 cols.push( { rel : false, abs : w });
9107                 total_abs += w;
9108                 last = i; // not really..
9109                 continue;
9110             }
9111             var w = cm.getColumnWidth(i, sz);
9112             if (w > 0) {
9113                 last = i
9114             }
9115             total += w;
9116             cols.push( { rel : w, abs : false });
9117         }
9118         
9119         var avail = this.bodyEl.dom.clientWidth - total_abs;
9120         
9121         var unitWidth = Math.floor(avail / total);
9122         var rem = avail - (unitWidth * total);
9123         
9124         var hidden, width, pos = 0 , splithide , left;
9125         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9126             
9127             hidden = 'display:none;';
9128             left = '';
9129             width  = 'width:0px;';
9130             splithide = '';
9131             if(!cm.isHidden(i)){
9132                 hidden = '';
9133                 
9134                 
9135                 // we can honour xs/sm/md/xl ?
9136                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9137                 if (w===0) {
9138                     hidden = 'display:none;';
9139                 }
9140                 // width should return a small number...
9141                 if (i == last) {
9142                     w+=rem; // add the remaining with..
9143                 }
9144                 pos += w;
9145                 left = "left:" + (pos -4) + "px;";
9146                 width = "width:" + w+ "px;";
9147                 
9148             }
9149             if (this.responsive) {
9150                 width = '';
9151                 left = '';
9152                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9153                 splithide = 'display: none;';
9154             }
9155             
9156             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9157             if (this.headEl) {
9158                 if (i == last) {
9159                     splithide = 'display:none;';
9160                 }
9161                 
9162                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9163                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9164                 );
9165             }
9166             
9167         }
9168         //Roo.log(styles.join(''));
9169         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9170         
9171     },
9172     
9173     
9174     
9175     onContextMenu : function(e, t)
9176     {
9177         this.processEvent("contextmenu", e);
9178     },
9179     
9180     processEvent : function(name, e)
9181     {
9182         if (name != 'touchstart' ) {
9183             this.fireEvent(name, e);    
9184         }
9185         
9186         var t = e.getTarget();
9187         
9188         var cell = Roo.get(t);
9189         
9190         if(!cell){
9191             return;
9192         }
9193         
9194         if(cell.findParent('tfoot', false, true)){
9195             return;
9196         }
9197         
9198         if(cell.findParent('thead', false, true)){
9199             
9200             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9201                 cell = Roo.get(t).findParent('th', false, true);
9202                 if (!cell) {
9203                     Roo.log("failed to find th in thead?");
9204                     Roo.log(e.getTarget());
9205                     return;
9206                 }
9207             }
9208             
9209             var cellIndex = cell.dom.cellIndex;
9210             
9211             var ename = name == 'touchstart' ? 'click' : name;
9212             this.fireEvent("header" + ename, this, cellIndex, e);
9213             
9214             return;
9215         }
9216         
9217         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9218             cell = Roo.get(t).findParent('td', false, true);
9219             if (!cell) {
9220                 Roo.log("failed to find th in tbody?");
9221                 Roo.log(e.getTarget());
9222                 return;
9223             }
9224         }
9225         
9226         var row = cell.findParent('tr', false, true);
9227         var cellIndex = cell.dom.cellIndex;
9228         var rowIndex = row.dom.rowIndex - 1;
9229         
9230         if(row !== false){
9231             
9232             this.fireEvent("row" + name, this, rowIndex, e);
9233             
9234             if(cell !== false){
9235             
9236                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9237             }
9238         }
9239         
9240     },
9241     
9242     onMouseover : function(e, el)
9243     {
9244         var cell = Roo.get(el);
9245         
9246         if(!cell){
9247             return;
9248         }
9249         
9250         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9251             cell = cell.findParent('td', false, true);
9252         }
9253         
9254         var row = cell.findParent('tr', false, true);
9255         var cellIndex = cell.dom.cellIndex;
9256         var rowIndex = row.dom.rowIndex - 1; // start from 0
9257         
9258         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9259         
9260     },
9261     
9262     onMouseout : function(e, el)
9263     {
9264         var cell = Roo.get(el);
9265         
9266         if(!cell){
9267             return;
9268         }
9269         
9270         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9271             cell = cell.findParent('td', false, true);
9272         }
9273         
9274         var row = cell.findParent('tr', false, true);
9275         var cellIndex = cell.dom.cellIndex;
9276         var rowIndex = row.dom.rowIndex - 1; // start from 0
9277         
9278         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9279         
9280     },
9281     
9282     onClick : function(e, el)
9283     {
9284         var cell = Roo.get(el);
9285         
9286         if(!cell || (!this.cellSelection && !this.rowSelection)){
9287             return;
9288         }
9289         
9290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9291             cell = cell.findParent('td', false, true);
9292         }
9293         
9294         if(!cell || typeof(cell) == 'undefined'){
9295             return;
9296         }
9297         
9298         var row = cell.findParent('tr', false, true);
9299         
9300         if(!row || typeof(row) == 'undefined'){
9301             return;
9302         }
9303         
9304         var cellIndex = cell.dom.cellIndex;
9305         var rowIndex = this.getRowIndex(row);
9306         
9307         // why??? - should these not be based on SelectionModel?
9308         //if(this.cellSelection){
9309             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9310         //}
9311         
9312         //if(this.rowSelection){
9313             this.fireEvent('rowclick', this, row, rowIndex, e);
9314         //}
9315          
9316     },
9317         
9318     onDblClick : function(e,el)
9319     {
9320         var cell = Roo.get(el);
9321         
9322         if(!cell || (!this.cellSelection && !this.rowSelection)){
9323             return;
9324         }
9325         
9326         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9327             cell = cell.findParent('td', false, true);
9328         }
9329         
9330         if(!cell || typeof(cell) == 'undefined'){
9331             return;
9332         }
9333         
9334         var row = cell.findParent('tr', false, true);
9335         
9336         if(!row || typeof(row) == 'undefined'){
9337             return;
9338         }
9339         
9340         var cellIndex = cell.dom.cellIndex;
9341         var rowIndex = this.getRowIndex(row);
9342         
9343         if(this.cellSelection){
9344             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9345         }
9346         
9347         if(this.rowSelection){
9348             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9349         }
9350     },
9351     findRowIndex : function(el)
9352     {
9353         var cell = Roo.get(el);
9354         if(!cell) {
9355             return false;
9356         }
9357         var row = cell.findParent('tr', false, true);
9358         
9359         if(!row || typeof(row) == 'undefined'){
9360             return false;
9361         }
9362         return this.getRowIndex(row);
9363     },
9364     sort : function(e,el)
9365     {
9366         var col = Roo.get(el);
9367         
9368         if(!col.hasClass('sortable')){
9369             return;
9370         }
9371         
9372         var sort = col.attr('sort');
9373         var dir = 'ASC';
9374         
9375         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9376             dir = 'DESC';
9377         }
9378         
9379         this.store.sortInfo = {field : sort, direction : dir};
9380         
9381         if (this.footer) {
9382             Roo.log("calling footer first");
9383             this.footer.onClick('first');
9384         } else {
9385         
9386             this.store.load({ params : { start : 0 } });
9387         }
9388     },
9389     
9390     renderHeader : function()
9391     {
9392         var header = {
9393             tag: 'thead',
9394             cn : []
9395         };
9396         
9397         var cm = this.cm;
9398         this.totalWidth = 0;
9399         
9400         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9401             
9402             var config = cm.config[i];
9403             
9404             var c = {
9405                 tag: 'th',
9406                 cls : 'x-hcol-' + i,
9407                 style : '',
9408                 
9409                 html: cm.getColumnHeader(i)
9410             };
9411             
9412             var tooltip = cm.getColumnTooltip(i);
9413             if (tooltip) {
9414                 c.tooltip = tooltip;
9415             }
9416             
9417             
9418             var hh = '';
9419             
9420             if(typeof(config.sortable) != 'undefined' && config.sortable){
9421                 c.cls += ' sortable';
9422                 c.html = '<i class="fa"></i>' + c.html;
9423             }
9424             
9425             // could use BS4 hidden-..-down 
9426             
9427             if(typeof(config.lgHeader) != 'undefined'){
9428                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9429             }
9430             
9431             if(typeof(config.mdHeader) != 'undefined'){
9432                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9433             }
9434             
9435             if(typeof(config.smHeader) != 'undefined'){
9436                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9437             }
9438             
9439             if(typeof(config.xsHeader) != 'undefined'){
9440                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9441             }
9442             
9443             if(hh.length){
9444                 c.html = hh;
9445             }
9446             
9447             if(typeof(config.tooltip) != 'undefined'){
9448                 c.tooltip = config.tooltip;
9449             }
9450             
9451             if(typeof(config.colspan) != 'undefined'){
9452                 c.colspan = config.colspan;
9453             }
9454             
9455             // hidden is handled by CSS now
9456             
9457             if(typeof(config.dataIndex) != 'undefined'){
9458                 c.sort = config.dataIndex;
9459             }
9460             
9461            
9462             
9463             if(typeof(config.align) != 'undefined' && config.align.length){
9464                 c.style += ' text-align:' + config.align + ';';
9465             }
9466             
9467             /* width is done in CSS
9468              *if(typeof(config.width) != 'undefined'){
9469                 c.style += ' width:' + config.width + 'px;';
9470                 this.totalWidth += config.width;
9471             } else {
9472                 this.totalWidth += 100; // assume minimum of 100 per column?
9473             }
9474             */
9475             
9476             if(typeof(config.cls) != 'undefined'){
9477                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9478             }
9479             // this is the bit that doesnt reall work at all...
9480             
9481             if (this.responsive) {
9482                  
9483             
9484                 ['xs','sm','md','lg'].map(function(size){
9485                     
9486                     if(typeof(config[size]) == 'undefined'){
9487                         return;
9488                     }
9489                      
9490                     if (!config[size]) { // 0 = hidden
9491                         // BS 4 '0' is treated as hide that column and below.
9492                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9493                         return;
9494                     }
9495                     
9496                     c.cls += ' col-' + size + '-' + config[size] + (
9497                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9498                     );
9499                     
9500                     
9501                 });
9502             }
9503             // at the end?
9504             
9505             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9506             
9507             
9508             
9509             
9510             header.cn.push(c)
9511         }
9512         
9513         return header;
9514     },
9515     
9516     renderBody : function()
9517     {
9518         var body = {
9519             tag: 'tbody',
9520             cn : [
9521                 {
9522                     tag: 'tr',
9523                     cn : [
9524                         {
9525                             tag : 'td',
9526                             colspan :  this.cm.getColumnCount()
9527                         }
9528                     ]
9529                 }
9530             ]
9531         };
9532         
9533         return body;
9534     },
9535     
9536     renderFooter : function()
9537     {
9538         var footer = {
9539             tag: 'tfoot',
9540             cn : [
9541                 {
9542                     tag: 'tr',
9543                     cn : [
9544                         {
9545                             tag : 'td',
9546                             colspan :  this.cm.getColumnCount()
9547                         }
9548                     ]
9549                 }
9550             ]
9551         };
9552         
9553         return footer;
9554     },
9555     
9556     
9557     
9558     onLoad : function()
9559     {
9560 //        Roo.log('ds onload');
9561         this.clear();
9562         
9563         var _this = this;
9564         var cm = this.cm;
9565         var ds = this.store;
9566         
9567         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9568             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9569             if (_this.store.sortInfo) {
9570                     
9571                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9572                     e.select('i', true).addClass(['fa-arrow-up']);
9573                 }
9574                 
9575                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9576                     e.select('i', true).addClass(['fa-arrow-down']);
9577                 }
9578             }
9579         });
9580         
9581         var tbody =  this.bodyEl;
9582               
9583         if(ds.getCount() > 0){
9584             ds.data.each(function(d,rowIndex){
9585                 var row =  this.renderRow(cm, ds, rowIndex);
9586                 
9587                 tbody.createChild(row);
9588                 
9589                 var _this = this;
9590                 
9591                 if(row.cellObjects.length){
9592                     Roo.each(row.cellObjects, function(r){
9593                         _this.renderCellObject(r);
9594                     })
9595                 }
9596                 
9597             }, this);
9598         }
9599         
9600         var tfoot = this.el.select('tfoot', true).first();
9601         
9602         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9603             
9604             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9605             
9606             var total = this.ds.getTotalCount();
9607             
9608             if(this.footer.pageSize < total){
9609                 this.mainFoot.show();
9610             }
9611         }
9612         
9613         Roo.each(this.el.select('tbody td', true).elements, function(e){
9614             e.on('mouseover', _this.onMouseover, _this);
9615         });
9616         
9617         Roo.each(this.el.select('tbody td', true).elements, function(e){
9618             e.on('mouseout', _this.onMouseout, _this);
9619         });
9620         this.fireEvent('rowsrendered', this);
9621         
9622         this.autoSize();
9623         
9624         this.initCSS(); /// resize cols
9625
9626         
9627     },
9628     
9629     
9630     onUpdate : function(ds,record)
9631     {
9632         this.refreshRow(record);
9633         this.autoSize();
9634     },
9635     
9636     onRemove : function(ds, record, index, isUpdate){
9637         if(isUpdate !== true){
9638             this.fireEvent("beforerowremoved", this, index, record);
9639         }
9640         var bt = this.bodyEl.dom;
9641         
9642         var rows = this.el.select('tbody > tr', true).elements;
9643         
9644         if(typeof(rows[index]) != 'undefined'){
9645             bt.removeChild(rows[index].dom);
9646         }
9647         
9648 //        if(bt.rows[index]){
9649 //            bt.removeChild(bt.rows[index]);
9650 //        }
9651         
9652         if(isUpdate !== true){
9653             //this.stripeRows(index);
9654             //this.syncRowHeights(index, index);
9655             //this.layout();
9656             this.fireEvent("rowremoved", this, index, record);
9657         }
9658     },
9659     
9660     onAdd : function(ds, records, rowIndex)
9661     {
9662         //Roo.log('on Add called');
9663         // - note this does not handle multiple adding very well..
9664         var bt = this.bodyEl.dom;
9665         for (var i =0 ; i < records.length;i++) {
9666             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9667             //Roo.log(records[i]);
9668             //Roo.log(this.store.getAt(rowIndex+i));
9669             this.insertRow(this.store, rowIndex + i, false);
9670             return;
9671         }
9672         
9673     },
9674     
9675     
9676     refreshRow : function(record){
9677         var ds = this.store, index;
9678         if(typeof record == 'number'){
9679             index = record;
9680             record = ds.getAt(index);
9681         }else{
9682             index = ds.indexOf(record);
9683             if (index < 0) {
9684                 return; // should not happen - but seems to 
9685             }
9686         }
9687         this.insertRow(ds, index, true);
9688         this.autoSize();
9689         this.onRemove(ds, record, index+1, true);
9690         this.autoSize();
9691         //this.syncRowHeights(index, index);
9692         //this.layout();
9693         this.fireEvent("rowupdated", this, index, record);
9694     },
9695     // private - called by RowSelection
9696     onRowSelect : function(rowIndex){
9697         var row = this.getRowDom(rowIndex);
9698         row.addClass(['bg-info','info']);
9699     },
9700     // private - called by RowSelection
9701     onRowDeselect : function(rowIndex)
9702     {
9703         if (rowIndex < 0) {
9704             return;
9705         }
9706         var row = this.getRowDom(rowIndex);
9707         row.removeClass(['bg-info','info']);
9708     },
9709       /**
9710      * Focuses the specified row.
9711      * @param {Number} row The row index
9712      */
9713     focusRow : function(row)
9714     {
9715         //Roo.log('GridView.focusRow');
9716         var x = this.bodyEl.dom.scrollLeft;
9717         this.focusCell(row, 0, false);
9718         this.bodyEl.dom.scrollLeft = x;
9719
9720     },
9721      /**
9722      * Focuses the specified cell.
9723      * @param {Number} row The row index
9724      * @param {Number} col The column index
9725      * @param {Boolean} hscroll false to disable horizontal scrolling
9726      */
9727     focusCell : function(row, col, hscroll)
9728     {
9729         //Roo.log('GridView.focusCell');
9730         var el = this.ensureVisible(row, col, hscroll);
9731         // not sure what focusEL achives = it's a <a> pos relative 
9732         //this.focusEl.alignTo(el, "tl-tl");
9733         //if(Roo.isGecko){
9734         //    this.focusEl.focus();
9735         //}else{
9736         //    this.focusEl.focus.defer(1, this.focusEl);
9737         //}
9738     },
9739     
9740      /**
9741      * Scrolls the specified cell into view
9742      * @param {Number} row The row index
9743      * @param {Number} col The column index
9744      * @param {Boolean} hscroll false to disable horizontal scrolling
9745      */
9746     ensureVisible : function(row, col, hscroll)
9747     {
9748         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9749         //return null; //disable for testing.
9750         if(typeof row != "number"){
9751             row = row.rowIndex;
9752         }
9753         if(row < 0 && row >= this.ds.getCount()){
9754             return  null;
9755         }
9756         col = (col !== undefined ? col : 0);
9757         var cm = this.cm;
9758         while(cm.isHidden(col)){
9759             col++;
9760         }
9761
9762         var el = this.getCellDom(row, col);
9763         if(!el){
9764             return null;
9765         }
9766         var c = this.bodyEl.dom;
9767
9768         var ctop = parseInt(el.offsetTop, 10);
9769         var cleft = parseInt(el.offsetLeft, 10);
9770         var cbot = ctop + el.offsetHeight;
9771         var cright = cleft + el.offsetWidth;
9772
9773         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9774         var ch = 0; //?? header is not withing the area?
9775         var stop = parseInt(c.scrollTop, 10);
9776         var sleft = parseInt(c.scrollLeft, 10);
9777         var sbot = stop + ch;
9778         var sright = sleft + c.clientWidth;
9779         /*
9780         Roo.log('GridView.ensureVisible:' +
9781                 ' ctop:' + ctop +
9782                 ' c.clientHeight:' + c.clientHeight +
9783                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9784                 ' stop:' + stop +
9785                 ' cbot:' + cbot +
9786                 ' sbot:' + sbot +
9787                 ' ch:' + ch  
9788                 );
9789         */
9790         if(ctop < stop){
9791             c.scrollTop = ctop;
9792             //Roo.log("set scrolltop to ctop DISABLE?");
9793         }else if(cbot > sbot){
9794             //Roo.log("set scrolltop to cbot-ch");
9795             c.scrollTop = cbot-ch;
9796         }
9797
9798         if(hscroll !== false){
9799             if(cleft < sleft){
9800                 c.scrollLeft = cleft;
9801             }else if(cright > sright){
9802                 c.scrollLeft = cright-c.clientWidth;
9803             }
9804         }
9805
9806         return el;
9807     },
9808     
9809     
9810     insertRow : function(dm, rowIndex, isUpdate){
9811         
9812         if(!isUpdate){
9813             this.fireEvent("beforerowsinserted", this, rowIndex);
9814         }
9815             //var s = this.getScrollState();
9816         var row = this.renderRow(this.cm, this.store, rowIndex);
9817         // insert before rowIndex..
9818         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9819         
9820         var _this = this;
9821                 
9822         if(row.cellObjects.length){
9823             Roo.each(row.cellObjects, function(r){
9824                 _this.renderCellObject(r);
9825             })
9826         }
9827             
9828         if(!isUpdate){
9829             this.fireEvent("rowsinserted", this, rowIndex);
9830             //this.syncRowHeights(firstRow, lastRow);
9831             //this.stripeRows(firstRow);
9832             //this.layout();
9833         }
9834         
9835     },
9836     
9837     
9838     getRowDom : function(rowIndex)
9839     {
9840         var rows = this.el.select('tbody > tr', true).elements;
9841         
9842         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9843         
9844     },
9845     getCellDom : function(rowIndex, colIndex)
9846     {
9847         var row = this.getRowDom(rowIndex);
9848         if (row === false) {
9849             return false;
9850         }
9851         var cols = row.select('td', true).elements;
9852         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9853         
9854     },
9855     
9856     // returns the object tree for a tr..
9857   
9858     
9859     renderRow : function(cm, ds, rowIndex) 
9860     {
9861         var d = ds.getAt(rowIndex);
9862         
9863         var row = {
9864             tag : 'tr',
9865             cls : 'x-row-' + rowIndex,
9866             cn : []
9867         };
9868             
9869         var cellObjects = [];
9870         
9871         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9872             var config = cm.config[i];
9873             
9874             var renderer = cm.getRenderer(i);
9875             var value = '';
9876             var id = false;
9877             
9878             if(typeof(renderer) !== 'undefined'){
9879                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9880             }
9881             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9882             // and are rendered into the cells after the row is rendered - using the id for the element.
9883             
9884             if(typeof(value) === 'object'){
9885                 id = Roo.id();
9886                 cellObjects.push({
9887                     container : id,
9888                     cfg : value 
9889                 })
9890             }
9891             
9892             var rowcfg = {
9893                 record: d,
9894                 rowIndex : rowIndex,
9895                 colIndex : i,
9896                 rowClass : ''
9897             };
9898
9899             this.fireEvent('rowclass', this, rowcfg);
9900             
9901             var td = {
9902                 tag: 'td',
9903                 // this might end up displaying HTML?
9904                 // this is too messy... - better to only do it on columsn you know are going to be too long
9905                 //tooltip : (typeof(value) === 'object') ? '' : value,
9906                 cls : rowcfg.rowClass + ' x-col-' + i,
9907                 style: '',
9908                 html: (typeof(value) === 'object') ? '' : value
9909             };
9910             
9911             if (id) {
9912                 td.id = id;
9913             }
9914             
9915             if(typeof(config.colspan) != 'undefined'){
9916                 td.colspan = config.colspan;
9917             }
9918             
9919             
9920             
9921             if(typeof(config.align) != 'undefined' && config.align.length){
9922                 td.style += ' text-align:' + config.align + ';';
9923             }
9924             if(typeof(config.valign) != 'undefined' && config.valign.length){
9925                 td.style += ' vertical-align:' + config.valign + ';';
9926             }
9927             /*
9928             if(typeof(config.width) != 'undefined'){
9929                 td.style += ' width:' +  config.width + 'px;';
9930             }
9931             */
9932             
9933             if(typeof(config.cursor) != 'undefined'){
9934                 td.style += ' cursor:' +  config.cursor + ';';
9935             }
9936             
9937             if(typeof(config.cls) != 'undefined'){
9938                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9939             }
9940             if (this.responsive) {
9941                 ['xs','sm','md','lg'].map(function(size){
9942                     
9943                     if(typeof(config[size]) == 'undefined'){
9944                         return;
9945                     }
9946                     
9947                     
9948                       
9949                     if (!config[size]) { // 0 = hidden
9950                         // BS 4 '0' is treated as hide that column and below.
9951                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9952                         return;
9953                     }
9954                     
9955                     td.cls += ' col-' + size + '-' + config[size] + (
9956                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9957                     );
9958                      
9959     
9960                 });
9961             }
9962             row.cn.push(td);
9963            
9964         }
9965         
9966         row.cellObjects = cellObjects;
9967         
9968         return row;
9969           
9970     },
9971     
9972     
9973     
9974     onBeforeLoad : function()
9975     {
9976         
9977     },
9978      /**
9979      * Remove all rows
9980      */
9981     clear : function()
9982     {
9983         this.el.select('tbody', true).first().dom.innerHTML = '';
9984     },
9985     /**
9986      * Show or hide a row.
9987      * @param {Number} rowIndex to show or hide
9988      * @param {Boolean} state hide
9989      */
9990     setRowVisibility : function(rowIndex, state)
9991     {
9992         var bt = this.bodyEl.dom;
9993         
9994         var rows = this.el.select('tbody > tr', true).elements;
9995         
9996         if(typeof(rows[rowIndex]) == 'undefined'){
9997             return;
9998         }
9999         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10000         
10001     },
10002     
10003     
10004     getSelectionModel : function(){
10005         if(!this.selModel){
10006             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10007         }
10008         return this.selModel;
10009     },
10010     /*
10011      * Render the Roo.bootstrap object from renderder
10012      */
10013     renderCellObject : function(r)
10014     {
10015         var _this = this;
10016         
10017         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10018         
10019         var t = r.cfg.render(r.container);
10020         
10021         if(r.cfg.cn){
10022             Roo.each(r.cfg.cn, function(c){
10023                 var child = {
10024                     container: t.getChildContainer(),
10025                     cfg: c
10026                 };
10027                 _this.renderCellObject(child);
10028             })
10029         }
10030     },
10031     /**
10032      * get the Row Index from a dom element.
10033      * @param {Roo.Element} row The row to look for
10034      * @returns {Number} the row
10035      */
10036     getRowIndex : function(row)
10037     {
10038         var rowIndex = -1;
10039         
10040         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10041             if(el != row){
10042                 return;
10043             }
10044             
10045             rowIndex = index;
10046         });
10047         
10048         return rowIndex;
10049     },
10050     /**
10051      * get the header TH element for columnIndex
10052      * @param {Number} columnIndex
10053      * @returns {Roo.Element}
10054      */
10055     getHeaderIndex: function(colIndex)
10056     {
10057         var cols = this.headEl.select('th', true).elements;
10058         return cols[colIndex]; 
10059     },
10060     /**
10061      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10062      * @param {domElement} cell to look for
10063      * @returns {Number} the column
10064      */
10065     getCellIndex : function(cell)
10066     {
10067         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10068         if(id){
10069             return parseInt(id[1], 10);
10070         }
10071         return 0;
10072     },
10073      /**
10074      * Returns the grid's underlying element = used by panel.Grid
10075      * @return {Element} The element
10076      */
10077     getGridEl : function(){
10078         return this.el;
10079     },
10080      /**
10081      * Forces a resize - used by panel.Grid
10082      * @return {Element} The element
10083      */
10084     autoSize : function()
10085     {
10086         //var ctr = Roo.get(this.container.dom.parentElement);
10087         var ctr = Roo.get(this.el.dom);
10088         
10089         var thd = this.getGridEl().select('thead',true).first();
10090         var tbd = this.getGridEl().select('tbody', true).first();
10091         var tfd = this.getGridEl().select('tfoot', true).first();
10092         
10093         var cw = ctr.getWidth();
10094         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10095         
10096         if (tbd) {
10097             
10098             tbd.setWidth(ctr.getWidth());
10099             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10100             // this needs fixing for various usage - currently only hydra job advers I think..
10101             //tdb.setHeight(
10102             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10103             //); 
10104             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10105             cw -= barsize;
10106         }
10107         cw = Math.max(cw, this.totalWidth);
10108         this.getGridEl().select('tbody tr',true).setWidth(cw);
10109         this.initCSS();
10110         
10111         // resize 'expandable coloumn?
10112         
10113         return; // we doe not have a view in this design..
10114         
10115     },
10116     onBodyScroll: function()
10117     {
10118         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10119         if(this.headEl){
10120             this.headEl.setStyle({
10121                 'position' : 'relative',
10122                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10123             });
10124         }
10125         
10126         if(this.lazyLoad){
10127             
10128             var scrollHeight = this.bodyEl.dom.scrollHeight;
10129             
10130             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10131             
10132             var height = this.bodyEl.getHeight();
10133             
10134             if(scrollHeight - height == scrollTop) {
10135                 
10136                 var total = this.ds.getTotalCount();
10137                 
10138                 if(this.footer.cursor + this.footer.pageSize < total){
10139                     
10140                     this.footer.ds.load({
10141                         params : {
10142                             start : this.footer.cursor + this.footer.pageSize,
10143                             limit : this.footer.pageSize
10144                         },
10145                         add : true
10146                     });
10147                 }
10148             }
10149             
10150         }
10151     },
10152     onColumnSplitterMoved : function(i, diff)
10153     {
10154         this.userResized = true;
10155         
10156         var cm = this.colModel;
10157         
10158         var w = this.getHeaderIndex(i).getWidth() + diff;
10159         
10160         
10161         cm.setColumnWidth(i, w, true);
10162         this.initCSS();
10163         //var cid = cm.getColumnId(i); << not used in this version?
10164        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10165         
10166         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10167         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10168         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10169 */
10170         //this.updateSplitters();
10171         //this.layout(); << ??
10172         this.fireEvent("columnresize", i, w);
10173     },
10174     onHeaderChange : function()
10175     {
10176         var header = this.renderHeader();
10177         var table = this.el.select('table', true).first();
10178         
10179         this.headEl.remove();
10180         this.headEl = table.createChild(header, this.bodyEl, false);
10181         
10182         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10183             e.on('click', this.sort, this);
10184         }, this);
10185         
10186         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10187             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10188         }
10189         
10190     },
10191     
10192     onHiddenChange : function(colModel, colIndex, hidden)
10193     {
10194         /*
10195         this.cm.setHidden()
10196         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10197         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10198         
10199         this.CSS.updateRule(thSelector, "display", "");
10200         this.CSS.updateRule(tdSelector, "display", "");
10201         
10202         if(hidden){
10203             this.CSS.updateRule(thSelector, "display", "none");
10204             this.CSS.updateRule(tdSelector, "display", "none");
10205         }
10206         */
10207         // onload calls initCSS()
10208         this.onHeaderChange();
10209         this.onLoad();
10210     },
10211     
10212     setColumnWidth: function(col_index, width)
10213     {
10214         // width = "md-2 xs-2..."
10215         if(!this.colModel.config[col_index]) {
10216             return;
10217         }
10218         
10219         var w = width.split(" ");
10220         
10221         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10222         
10223         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10224         
10225         
10226         for(var j = 0; j < w.length; j++) {
10227             
10228             if(!w[j]) {
10229                 continue;
10230             }
10231             
10232             var size_cls = w[j].split("-");
10233             
10234             if(!Number.isInteger(size_cls[1] * 1)) {
10235                 continue;
10236             }
10237             
10238             if(!this.colModel.config[col_index][size_cls[0]]) {
10239                 continue;
10240             }
10241             
10242             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10243                 continue;
10244             }
10245             
10246             h_row[0].classList.replace(
10247                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10248                 "col-"+size_cls[0]+"-"+size_cls[1]
10249             );
10250             
10251             for(var i = 0; i < rows.length; i++) {
10252                 
10253                 var size_cls = w[j].split("-");
10254                 
10255                 if(!Number.isInteger(size_cls[1] * 1)) {
10256                     continue;
10257                 }
10258                 
10259                 if(!this.colModel.config[col_index][size_cls[0]]) {
10260                     continue;
10261                 }
10262                 
10263                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10264                     continue;
10265                 }
10266                 
10267                 rows[i].classList.replace(
10268                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10269                     "col-"+size_cls[0]+"-"+size_cls[1]
10270                 );
10271             }
10272             
10273             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10274         }
10275     }
10276 });
10277
10278 // currently only used to find the split on drag.. 
10279 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10280
10281 /**
10282  * @depricated
10283 */
10284 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10285 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10286 /*
10287  * - LGPL
10288  *
10289  * table cell
10290  * 
10291  */
10292
10293 /**
10294  * @class Roo.bootstrap.TableCell
10295  * @extends Roo.bootstrap.Component
10296  * Bootstrap TableCell class
10297  * @cfg {String} html cell contain text
10298  * @cfg {String} cls cell class
10299  * @cfg {String} tag cell tag (td|th) default td
10300  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10301  * @cfg {String} align Aligns the content in a cell
10302  * @cfg {String} axis Categorizes cells
10303  * @cfg {String} bgcolor Specifies the background color of a cell
10304  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10305  * @cfg {Number} colspan Specifies the number of columns a cell should span
10306  * @cfg {String} headers Specifies one or more header cells a cell is related to
10307  * @cfg {Number} height Sets the height of a cell
10308  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10309  * @cfg {Number} rowspan Sets the number of rows a cell should span
10310  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10311  * @cfg {String} valign Vertical aligns the content in a cell
10312  * @cfg {Number} width Specifies the width of a cell
10313  * 
10314  * @constructor
10315  * Create a new TableCell
10316  * @param {Object} config The config object
10317  */
10318
10319 Roo.bootstrap.TableCell = function(config){
10320     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10321 };
10322
10323 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10324     
10325     html: false,
10326     cls: false,
10327     tag: false,
10328     abbr: false,
10329     align: false,
10330     axis: false,
10331     bgcolor: false,
10332     charoff: false,
10333     colspan: false,
10334     headers: false,
10335     height: false,
10336     nowrap: false,
10337     rowspan: false,
10338     scope: false,
10339     valign: false,
10340     width: false,
10341     
10342     
10343     getAutoCreate : function(){
10344         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10345         
10346         cfg = {
10347             tag: 'td'
10348         };
10349         
10350         if(this.tag){
10351             cfg.tag = this.tag;
10352         }
10353         
10354         if (this.html) {
10355             cfg.html=this.html
10356         }
10357         if (this.cls) {
10358             cfg.cls=this.cls
10359         }
10360         if (this.abbr) {
10361             cfg.abbr=this.abbr
10362         }
10363         if (this.align) {
10364             cfg.align=this.align
10365         }
10366         if (this.axis) {
10367             cfg.axis=this.axis
10368         }
10369         if (this.bgcolor) {
10370             cfg.bgcolor=this.bgcolor
10371         }
10372         if (this.charoff) {
10373             cfg.charoff=this.charoff
10374         }
10375         if (this.colspan) {
10376             cfg.colspan=this.colspan
10377         }
10378         if (this.headers) {
10379             cfg.headers=this.headers
10380         }
10381         if (this.height) {
10382             cfg.height=this.height
10383         }
10384         if (this.nowrap) {
10385             cfg.nowrap=this.nowrap
10386         }
10387         if (this.rowspan) {
10388             cfg.rowspan=this.rowspan
10389         }
10390         if (this.scope) {
10391             cfg.scope=this.scope
10392         }
10393         if (this.valign) {
10394             cfg.valign=this.valign
10395         }
10396         if (this.width) {
10397             cfg.width=this.width
10398         }
10399         
10400         
10401         return cfg;
10402     }
10403    
10404 });
10405
10406  
10407
10408  /*
10409  * - LGPL
10410  *
10411  * table row
10412  * 
10413  */
10414
10415 /**
10416  * @class Roo.bootstrap.TableRow
10417  * @extends Roo.bootstrap.Component
10418  * Bootstrap TableRow class
10419  * @cfg {String} cls row class
10420  * @cfg {String} align Aligns the content in a table row
10421  * @cfg {String} bgcolor Specifies a background color for a table row
10422  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10423  * @cfg {String} valign Vertical aligns the content in a table row
10424  * 
10425  * @constructor
10426  * Create a new TableRow
10427  * @param {Object} config The config object
10428  */
10429
10430 Roo.bootstrap.TableRow = function(config){
10431     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10432 };
10433
10434 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10435     
10436     cls: false,
10437     align: false,
10438     bgcolor: false,
10439     charoff: false,
10440     valign: false,
10441     
10442     getAutoCreate : function(){
10443         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10444         
10445         cfg = {
10446             tag: 'tr'
10447         };
10448             
10449         if(this.cls){
10450             cfg.cls = this.cls;
10451         }
10452         if(this.align){
10453             cfg.align = this.align;
10454         }
10455         if(this.bgcolor){
10456             cfg.bgcolor = this.bgcolor;
10457         }
10458         if(this.charoff){
10459             cfg.charoff = this.charoff;
10460         }
10461         if(this.valign){
10462             cfg.valign = this.valign;
10463         }
10464         
10465         return cfg;
10466     }
10467    
10468 });
10469
10470  
10471
10472  /*
10473  * - LGPL
10474  *
10475  * table body
10476  * 
10477  */
10478
10479 /**
10480  * @class Roo.bootstrap.TableBody
10481  * @extends Roo.bootstrap.Component
10482  * Bootstrap TableBody class
10483  * @cfg {String} cls element class
10484  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10485  * @cfg {String} align Aligns the content inside the element
10486  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10487  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10488  * 
10489  * @constructor
10490  * Create a new TableBody
10491  * @param {Object} config The config object
10492  */
10493
10494 Roo.bootstrap.TableBody = function(config){
10495     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10496 };
10497
10498 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10499     
10500     cls: false,
10501     tag: false,
10502     align: false,
10503     charoff: false,
10504     valign: false,
10505     
10506     getAutoCreate : function(){
10507         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10508         
10509         cfg = {
10510             tag: 'tbody'
10511         };
10512             
10513         if (this.cls) {
10514             cfg.cls=this.cls
10515         }
10516         if(this.tag){
10517             cfg.tag = this.tag;
10518         }
10519         
10520         if(this.align){
10521             cfg.align = this.align;
10522         }
10523         if(this.charoff){
10524             cfg.charoff = this.charoff;
10525         }
10526         if(this.valign){
10527             cfg.valign = this.valign;
10528         }
10529         
10530         return cfg;
10531     }
10532     
10533     
10534 //    initEvents : function()
10535 //    {
10536 //        
10537 //        if(!this.store){
10538 //            return;
10539 //        }
10540 //        
10541 //        this.store = Roo.factory(this.store, Roo.data);
10542 //        this.store.on('load', this.onLoad, this);
10543 //        
10544 //        this.store.load();
10545 //        
10546 //    },
10547 //    
10548 //    onLoad: function () 
10549 //    {   
10550 //        this.fireEvent('load', this);
10551 //    }
10552 //    
10553 //   
10554 });
10555
10556  
10557
10558  /*
10559  * Based on:
10560  * Ext JS Library 1.1.1
10561  * Copyright(c) 2006-2007, Ext JS, LLC.
10562  *
10563  * Originally Released Under LGPL - original licence link has changed is not relivant.
10564  *
10565  * Fork - LGPL
10566  * <script type="text/javascript">
10567  */
10568
10569 // as we use this in bootstrap.
10570 Roo.namespace('Roo.form');
10571  /**
10572  * @class Roo.form.Action
10573  * Internal Class used to handle form actions
10574  * @constructor
10575  * @param {Roo.form.BasicForm} el The form element or its id
10576  * @param {Object} config Configuration options
10577  */
10578
10579  
10580  
10581 // define the action interface
10582 Roo.form.Action = function(form, options){
10583     this.form = form;
10584     this.options = options || {};
10585 };
10586 /**
10587  * Client Validation Failed
10588  * @const 
10589  */
10590 Roo.form.Action.CLIENT_INVALID = 'client';
10591 /**
10592  * Server Validation Failed
10593  * @const 
10594  */
10595 Roo.form.Action.SERVER_INVALID = 'server';
10596  /**
10597  * Connect to Server Failed
10598  * @const 
10599  */
10600 Roo.form.Action.CONNECT_FAILURE = 'connect';
10601 /**
10602  * Reading Data from Server Failed
10603  * @const 
10604  */
10605 Roo.form.Action.LOAD_FAILURE = 'load';
10606
10607 Roo.form.Action.prototype = {
10608     type : 'default',
10609     failureType : undefined,
10610     response : undefined,
10611     result : undefined,
10612
10613     // interface method
10614     run : function(options){
10615
10616     },
10617
10618     // interface method
10619     success : function(response){
10620
10621     },
10622
10623     // interface method
10624     handleResponse : function(response){
10625
10626     },
10627
10628     // default connection failure
10629     failure : function(response){
10630         
10631         this.response = response;
10632         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10633         this.form.afterAction(this, false);
10634     },
10635
10636     processResponse : function(response){
10637         this.response = response;
10638         if(!response.responseText){
10639             return true;
10640         }
10641         this.result = this.handleResponse(response);
10642         return this.result;
10643     },
10644
10645     // utility functions used internally
10646     getUrl : function(appendParams){
10647         var url = this.options.url || this.form.url || this.form.el.dom.action;
10648         if(appendParams){
10649             var p = this.getParams();
10650             if(p){
10651                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10652             }
10653         }
10654         return url;
10655     },
10656
10657     getMethod : function(){
10658         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10659     },
10660
10661     getParams : function(){
10662         var bp = this.form.baseParams;
10663         var p = this.options.params;
10664         if(p){
10665             if(typeof p == "object"){
10666                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10667             }else if(typeof p == 'string' && bp){
10668                 p += '&' + Roo.urlEncode(bp);
10669             }
10670         }else if(bp){
10671             p = Roo.urlEncode(bp);
10672         }
10673         return p;
10674     },
10675
10676     createCallback : function(){
10677         return {
10678             success: this.success,
10679             failure: this.failure,
10680             scope: this,
10681             timeout: (this.form.timeout*1000),
10682             upload: this.form.fileUpload ? this.success : undefined
10683         };
10684     }
10685 };
10686
10687 Roo.form.Action.Submit = function(form, options){
10688     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10689 };
10690
10691 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10692     type : 'submit',
10693
10694     haveProgress : false,
10695     uploadComplete : false,
10696     
10697     // uploadProgress indicator.
10698     uploadProgress : function()
10699     {
10700         if (!this.form.progressUrl) {
10701             return;
10702         }
10703         
10704         if (!this.haveProgress) {
10705             Roo.MessageBox.progress("Uploading", "Uploading");
10706         }
10707         if (this.uploadComplete) {
10708            Roo.MessageBox.hide();
10709            return;
10710         }
10711         
10712         this.haveProgress = true;
10713    
10714         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10715         
10716         var c = new Roo.data.Connection();
10717         c.request({
10718             url : this.form.progressUrl,
10719             params: {
10720                 id : uid
10721             },
10722             method: 'GET',
10723             success : function(req){
10724                //console.log(data);
10725                 var rdata = false;
10726                 var edata;
10727                 try  {
10728                    rdata = Roo.decode(req.responseText)
10729                 } catch (e) {
10730                     Roo.log("Invalid data from server..");
10731                     Roo.log(edata);
10732                     return;
10733                 }
10734                 if (!rdata || !rdata.success) {
10735                     Roo.log(rdata);
10736                     Roo.MessageBox.alert(Roo.encode(rdata));
10737                     return;
10738                 }
10739                 var data = rdata.data;
10740                 
10741                 if (this.uploadComplete) {
10742                    Roo.MessageBox.hide();
10743                    return;
10744                 }
10745                    
10746                 if (data){
10747                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10748                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10749                     );
10750                 }
10751                 this.uploadProgress.defer(2000,this);
10752             },
10753        
10754             failure: function(data) {
10755                 Roo.log('progress url failed ');
10756                 Roo.log(data);
10757             },
10758             scope : this
10759         });
10760            
10761     },
10762     
10763     
10764     run : function()
10765     {
10766         // run get Values on the form, so it syncs any secondary forms.
10767         this.form.getValues();
10768         
10769         var o = this.options;
10770         var method = this.getMethod();
10771         var isPost = method == 'POST';
10772         if(o.clientValidation === false || this.form.isValid()){
10773             
10774             if (this.form.progressUrl) {
10775                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10776                     (new Date() * 1) + '' + Math.random());
10777                     
10778             } 
10779             
10780             
10781             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10782                 form:this.form.el.dom,
10783                 url:this.getUrl(!isPost),
10784                 method: method,
10785                 params:isPost ? this.getParams() : null,
10786                 isUpload: this.form.fileUpload,
10787                 formData : this.form.formData
10788             }));
10789             
10790             this.uploadProgress();
10791
10792         }else if (o.clientValidation !== false){ // client validation failed
10793             this.failureType = Roo.form.Action.CLIENT_INVALID;
10794             this.form.afterAction(this, false);
10795         }
10796     },
10797
10798     success : function(response)
10799     {
10800         this.uploadComplete= true;
10801         if (this.haveProgress) {
10802             Roo.MessageBox.hide();
10803         }
10804         
10805         
10806         var result = this.processResponse(response);
10807         if(result === true || result.success){
10808             this.form.afterAction(this, true);
10809             return;
10810         }
10811         if(result.errors){
10812             this.form.markInvalid(result.errors);
10813             this.failureType = Roo.form.Action.SERVER_INVALID;
10814         }
10815         this.form.afterAction(this, false);
10816     },
10817     failure : function(response)
10818     {
10819         this.uploadComplete= true;
10820         if (this.haveProgress) {
10821             Roo.MessageBox.hide();
10822         }
10823         
10824         this.response = response;
10825         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10826         this.form.afterAction(this, false);
10827     },
10828     
10829     handleResponse : function(response){
10830         if(this.form.errorReader){
10831             var rs = this.form.errorReader.read(response);
10832             var errors = [];
10833             if(rs.records){
10834                 for(var i = 0, len = rs.records.length; i < len; i++) {
10835                     var r = rs.records[i];
10836                     errors[i] = r.data;
10837                 }
10838             }
10839             if(errors.length < 1){
10840                 errors = null;
10841             }
10842             return {
10843                 success : rs.success,
10844                 errors : errors
10845             };
10846         }
10847         var ret = false;
10848         try {
10849             ret = Roo.decode(response.responseText);
10850         } catch (e) {
10851             ret = {
10852                 success: false,
10853                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10854                 errors : []
10855             };
10856         }
10857         return ret;
10858         
10859     }
10860 });
10861
10862
10863 Roo.form.Action.Load = function(form, options){
10864     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10865     this.reader = this.form.reader;
10866 };
10867
10868 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10869     type : 'load',
10870
10871     run : function(){
10872         
10873         Roo.Ajax.request(Roo.apply(
10874                 this.createCallback(), {
10875                     method:this.getMethod(),
10876                     url:this.getUrl(false),
10877                     params:this.getParams()
10878         }));
10879     },
10880
10881     success : function(response){
10882         
10883         var result = this.processResponse(response);
10884         if(result === true || !result.success || !result.data){
10885             this.failureType = Roo.form.Action.LOAD_FAILURE;
10886             this.form.afterAction(this, false);
10887             return;
10888         }
10889         this.form.clearInvalid();
10890         this.form.setValues(result.data);
10891         this.form.afterAction(this, true);
10892     },
10893
10894     handleResponse : function(response){
10895         if(this.form.reader){
10896             var rs = this.form.reader.read(response);
10897             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10898             return {
10899                 success : rs.success,
10900                 data : data
10901             };
10902         }
10903         return Roo.decode(response.responseText);
10904     }
10905 });
10906
10907 Roo.form.Action.ACTION_TYPES = {
10908     'load' : Roo.form.Action.Load,
10909     'submit' : Roo.form.Action.Submit
10910 };/*
10911  * - LGPL
10912  *
10913  * form
10914  *
10915  */
10916
10917 /**
10918  * @class Roo.bootstrap.Form
10919  * @extends Roo.bootstrap.Component
10920  * @children Roo.bootstrap.Component
10921  * Bootstrap Form class
10922  * @cfg {String} method  GET | POST (default POST)
10923  * @cfg {String} labelAlign top | left (default top)
10924  * @cfg {String} align left  | right - for navbars
10925  * @cfg {Boolean} loadMask load mask when submit (default true)
10926
10927  *
10928  * @constructor
10929  * Create a new Form
10930  * @param {Object} config The config object
10931  */
10932
10933
10934 Roo.bootstrap.Form = function(config){
10935     
10936     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10937     
10938     Roo.bootstrap.Form.popover.apply();
10939     
10940     this.addEvents({
10941         /**
10942          * @event clientvalidation
10943          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10944          * @param {Form} this
10945          * @param {Boolean} valid true if the form has passed client-side validation
10946          */
10947         clientvalidation: true,
10948         /**
10949          * @event beforeaction
10950          * Fires before any action is performed. Return false to cancel the action.
10951          * @param {Form} this
10952          * @param {Action} action The action to be performed
10953          */
10954         beforeaction: true,
10955         /**
10956          * @event actionfailed
10957          * Fires when an action fails.
10958          * @param {Form} this
10959          * @param {Action} action The action that failed
10960          */
10961         actionfailed : true,
10962         /**
10963          * @event actioncomplete
10964          * Fires when an action is completed.
10965          * @param {Form} this
10966          * @param {Action} action The action that completed
10967          */
10968         actioncomplete : true
10969     });
10970 };
10971
10972 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10973
10974      /**
10975      * @cfg {String} method
10976      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10977      */
10978     method : 'POST',
10979     /**
10980      * @cfg {String} url
10981      * The URL to use for form actions if one isn't supplied in the action options.
10982      */
10983     /**
10984      * @cfg {Boolean} fileUpload
10985      * Set to true if this form is a file upload.
10986      */
10987
10988     /**
10989      * @cfg {Object} baseParams
10990      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10991      */
10992
10993     /**
10994      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10995      */
10996     timeout: 30,
10997     /**
10998      * @cfg {Sting} align (left|right) for navbar forms
10999      */
11000     align : 'left',
11001
11002     // private
11003     activeAction : null,
11004
11005     /**
11006      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11007      * element by passing it or its id or mask the form itself by passing in true.
11008      * @type Mixed
11009      */
11010     waitMsgTarget : false,
11011
11012     loadMask : true,
11013     
11014     /**
11015      * @cfg {Boolean} errorMask (true|false) default false
11016      */
11017     errorMask : false,
11018     
11019     /**
11020      * @cfg {Number} maskOffset Default 100
11021      */
11022     maskOffset : 100,
11023     
11024     /**
11025      * @cfg {Boolean} maskBody
11026      */
11027     maskBody : false,
11028
11029     getAutoCreate : function(){
11030
11031         var cfg = {
11032             tag: 'form',
11033             method : this.method || 'POST',
11034             id : this.id || Roo.id(),
11035             cls : ''
11036         };
11037         if (this.parent().xtype.match(/^Nav/)) {
11038             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11039
11040         }
11041
11042         if (this.labelAlign == 'left' ) {
11043             cfg.cls += ' form-horizontal';
11044         }
11045
11046
11047         return cfg;
11048     },
11049     initEvents : function()
11050     {
11051         this.el.on('submit', this.onSubmit, this);
11052         // this was added as random key presses on the form where triggering form submit.
11053         this.el.on('keypress', function(e) {
11054             if (e.getCharCode() != 13) {
11055                 return true;
11056             }
11057             // we might need to allow it for textareas.. and some other items.
11058             // check e.getTarget().
11059
11060             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11061                 return true;
11062             }
11063
11064             Roo.log("keypress blocked");
11065
11066             e.preventDefault();
11067             return false;
11068         });
11069         
11070     },
11071     // private
11072     onSubmit : function(e){
11073         e.stopEvent();
11074     },
11075
11076      /**
11077      * Returns true if client-side validation on the form is successful.
11078      * @return Boolean
11079      */
11080     isValid : function(){
11081         var items = this.getItems();
11082         var valid = true;
11083         var target = false;
11084         
11085         items.each(function(f){
11086             
11087             if(f.validate()){
11088                 return;
11089             }
11090             
11091             Roo.log('invalid field: ' + f.name);
11092             
11093             valid = false;
11094
11095             if(!target && f.el.isVisible(true)){
11096                 target = f;
11097             }
11098            
11099         });
11100         
11101         if(this.errorMask && !valid){
11102             Roo.bootstrap.Form.popover.mask(this, target);
11103         }
11104         
11105         return valid;
11106     },
11107     
11108     /**
11109      * Returns true if any fields in this form have changed since their original load.
11110      * @return Boolean
11111      */
11112     isDirty : function(){
11113         var dirty = false;
11114         var items = this.getItems();
11115         items.each(function(f){
11116            if(f.isDirty()){
11117                dirty = true;
11118                return false;
11119            }
11120            return true;
11121         });
11122         return dirty;
11123     },
11124      /**
11125      * Performs a predefined action (submit or load) or custom actions you define on this form.
11126      * @param {String} actionName The name of the action type
11127      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11128      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11129      * accept other config options):
11130      * <pre>
11131 Property          Type             Description
11132 ----------------  ---------------  ----------------------------------------------------------------------------------
11133 url               String           The url for the action (defaults to the form's url)
11134 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11135 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11136 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11137                                    validate the form on the client (defaults to false)
11138      * </pre>
11139      * @return {BasicForm} this
11140      */
11141     doAction : function(action, options){
11142         if(typeof action == 'string'){
11143             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11144         }
11145         if(this.fireEvent('beforeaction', this, action) !== false){
11146             this.beforeAction(action);
11147             action.run.defer(100, action);
11148         }
11149         return this;
11150     },
11151
11152     // private
11153     beforeAction : function(action){
11154         var o = action.options;
11155         
11156         if(this.loadMask){
11157             
11158             if(this.maskBody){
11159                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11160             } else {
11161                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11162             }
11163         }
11164         // not really supported yet.. ??
11165
11166         //if(this.waitMsgTarget === true){
11167         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11168         //}else if(this.waitMsgTarget){
11169         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11170         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11171         //}else {
11172         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11173        // }
11174
11175     },
11176
11177     // private
11178     afterAction : function(action, success){
11179         this.activeAction = null;
11180         var o = action.options;
11181
11182         if(this.loadMask){
11183             
11184             if(this.maskBody){
11185                 Roo.get(document.body).unmask();
11186             } else {
11187                 this.el.unmask();
11188             }
11189         }
11190         
11191         //if(this.waitMsgTarget === true){
11192 //            this.el.unmask();
11193         //}else if(this.waitMsgTarget){
11194         //    this.waitMsgTarget.unmask();
11195         //}else{
11196         //    Roo.MessageBox.updateProgress(1);
11197         //    Roo.MessageBox.hide();
11198        // }
11199         //
11200         if(success){
11201             if(o.reset){
11202                 this.reset();
11203             }
11204             Roo.callback(o.success, o.scope, [this, action]);
11205             this.fireEvent('actioncomplete', this, action);
11206
11207         }else{
11208
11209             // failure condition..
11210             // we have a scenario where updates need confirming.
11211             // eg. if a locking scenario exists..
11212             // we look for { errors : { needs_confirm : true }} in the response.
11213             if (
11214                 (typeof(action.result) != 'undefined')  &&
11215                 (typeof(action.result.errors) != 'undefined')  &&
11216                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11217            ){
11218                 var _t = this;
11219                 Roo.log("not supported yet");
11220                  /*
11221
11222                 Roo.MessageBox.confirm(
11223                     "Change requires confirmation",
11224                     action.result.errorMsg,
11225                     function(r) {
11226                         if (r != 'yes') {
11227                             return;
11228                         }
11229                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11230                     }
11231
11232                 );
11233                 */
11234
11235
11236                 return;
11237             }
11238
11239             Roo.callback(o.failure, o.scope, [this, action]);
11240             // show an error message if no failed handler is set..
11241             if (!this.hasListener('actionfailed')) {
11242                 Roo.log("need to add dialog support");
11243                 /*
11244                 Roo.MessageBox.alert("Error",
11245                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11246                         action.result.errorMsg :
11247                         "Saving Failed, please check your entries or try again"
11248                 );
11249                 */
11250             }
11251
11252             this.fireEvent('actionfailed', this, action);
11253         }
11254
11255     },
11256     /**
11257      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11258      * @param {String} id The value to search for
11259      * @return Field
11260      */
11261     findField : function(id){
11262         var items = this.getItems();
11263         var field = items.get(id);
11264         if(!field){
11265              items.each(function(f){
11266                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11267                     field = f;
11268                     return false;
11269                 }
11270                 return true;
11271             });
11272         }
11273         return field || null;
11274     },
11275      /**
11276      * Mark fields in this form invalid in bulk.
11277      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11278      * @return {BasicForm} this
11279      */
11280     markInvalid : function(errors){
11281         if(errors instanceof Array){
11282             for(var i = 0, len = errors.length; i < len; i++){
11283                 var fieldError = errors[i];
11284                 var f = this.findField(fieldError.id);
11285                 if(f){
11286                     f.markInvalid(fieldError.msg);
11287                 }
11288             }
11289         }else{
11290             var field, id;
11291             for(id in errors){
11292                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11293                     field.markInvalid(errors[id]);
11294                 }
11295             }
11296         }
11297         //Roo.each(this.childForms || [], function (f) {
11298         //    f.markInvalid(errors);
11299         //});
11300
11301         return this;
11302     },
11303
11304     /**
11305      * Set values for fields in this form in bulk.
11306      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11307      * @return {BasicForm} this
11308      */
11309     setValues : function(values){
11310         if(values instanceof Array){ // array of objects
11311             for(var i = 0, len = values.length; i < len; i++){
11312                 var v = values[i];
11313                 var f = this.findField(v.id);
11314                 if(f){
11315                     f.setValue(v.value);
11316                     if(this.trackResetOnLoad){
11317                         f.originalValue = f.getValue();
11318                     }
11319                 }
11320             }
11321         }else{ // object hash
11322             var field, id;
11323             for(id in values){
11324                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11325
11326                     if (field.setFromData &&
11327                         field.valueField &&
11328                         field.displayField &&
11329                         // combos' with local stores can
11330                         // be queried via setValue()
11331                         // to set their value..
11332                         (field.store && !field.store.isLocal)
11333                         ) {
11334                         // it's a combo
11335                         var sd = { };
11336                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11337                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11338                         field.setFromData(sd);
11339
11340                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11341                         
11342                         field.setFromData(values);
11343                         
11344                     } else {
11345                         field.setValue(values[id]);
11346                     }
11347
11348
11349                     if(this.trackResetOnLoad){
11350                         field.originalValue = field.getValue();
11351                     }
11352                 }
11353             }
11354         }
11355
11356         //Roo.each(this.childForms || [], function (f) {
11357         //    f.setValues(values);
11358         //});
11359
11360         return this;
11361     },
11362
11363     /**
11364      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11365      * they are returned as an array.
11366      * @param {Boolean} asString
11367      * @return {Object}
11368      */
11369     getValues : function(asString){
11370         //if (this.childForms) {
11371             // copy values from the child forms
11372         //    Roo.each(this.childForms, function (f) {
11373         //        this.setValues(f.getValues());
11374         //    }, this);
11375         //}
11376
11377
11378
11379         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11380         if(asString === true){
11381             return fs;
11382         }
11383         return Roo.urlDecode(fs);
11384     },
11385
11386     /**
11387      * Returns the fields in this form as an object with key/value pairs.
11388      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11389      * @return {Object}
11390      */
11391     getFieldValues : function(with_hidden)
11392     {
11393         var items = this.getItems();
11394         var ret = {};
11395         items.each(function(f){
11396             
11397             if (!f.getName()) {
11398                 return;
11399             }
11400             
11401             var v = f.getValue();
11402             
11403             if (f.inputType =='radio') {
11404                 if (typeof(ret[f.getName()]) == 'undefined') {
11405                     ret[f.getName()] = ''; // empty..
11406                 }
11407
11408                 if (!f.el.dom.checked) {
11409                     return;
11410
11411                 }
11412                 v = f.el.dom.value;
11413
11414             }
11415             
11416             if(f.xtype == 'MoneyField'){
11417                 ret[f.currencyName] = f.getCurrency();
11418             }
11419
11420             // not sure if this supported any more..
11421             if ((typeof(v) == 'object') && f.getRawValue) {
11422                 v = f.getRawValue() ; // dates..
11423             }
11424             // combo boxes where name != hiddenName...
11425             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11426                 ret[f.name] = f.getRawValue();
11427             }
11428             ret[f.getName()] = v;
11429         });
11430
11431         return ret;
11432     },
11433
11434     /**
11435      * Clears all invalid messages in this form.
11436      * @return {BasicForm} this
11437      */
11438     clearInvalid : function(){
11439         var items = this.getItems();
11440
11441         items.each(function(f){
11442            f.clearInvalid();
11443         });
11444
11445         return this;
11446     },
11447
11448     /**
11449      * Resets this form.
11450      * @return {BasicForm} this
11451      */
11452     reset : function(){
11453         var items = this.getItems();
11454         items.each(function(f){
11455             f.reset();
11456         });
11457
11458         Roo.each(this.childForms || [], function (f) {
11459             f.reset();
11460         });
11461
11462
11463         return this;
11464     },
11465     
11466     getItems : function()
11467     {
11468         var r=new Roo.util.MixedCollection(false, function(o){
11469             return o.id || (o.id = Roo.id());
11470         });
11471         var iter = function(el) {
11472             if (el.inputEl) {
11473                 r.add(el);
11474             }
11475             if (!el.items) {
11476                 return;
11477             }
11478             Roo.each(el.items,function(e) {
11479                 iter(e);
11480             });
11481         };
11482
11483         iter(this);
11484         return r;
11485     },
11486     
11487     hideFields : function(items)
11488     {
11489         Roo.each(items, function(i){
11490             
11491             var f = this.findField(i);
11492             
11493             if(!f){
11494                 return;
11495             }
11496             
11497             f.hide();
11498             
11499         }, this);
11500     },
11501     
11502     showFields : function(items)
11503     {
11504         Roo.each(items, function(i){
11505             
11506             var f = this.findField(i);
11507             
11508             if(!f){
11509                 return;
11510             }
11511             
11512             f.show();
11513             
11514         }, this);
11515     }
11516
11517 });
11518
11519 Roo.apply(Roo.bootstrap.Form, {
11520     
11521     popover : {
11522         
11523         padding : 5,
11524         
11525         isApplied : false,
11526         
11527         isMasked : false,
11528         
11529         form : false,
11530         
11531         target : false,
11532         
11533         toolTip : false,
11534         
11535         intervalID : false,
11536         
11537         maskEl : false,
11538         
11539         apply : function()
11540         {
11541             if(this.isApplied){
11542                 return;
11543             }
11544             
11545             this.maskEl = {
11546                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11547                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11548                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11549                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11550             };
11551             
11552             this.maskEl.top.enableDisplayMode("block");
11553             this.maskEl.left.enableDisplayMode("block");
11554             this.maskEl.bottom.enableDisplayMode("block");
11555             this.maskEl.right.enableDisplayMode("block");
11556             
11557             this.toolTip = new Roo.bootstrap.Tooltip({
11558                 cls : 'roo-form-error-popover',
11559                 alignment : {
11560                     'left' : ['r-l', [-2,0], 'right'],
11561                     'right' : ['l-r', [2,0], 'left'],
11562                     'bottom' : ['tl-bl', [0,2], 'top'],
11563                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11564                 }
11565             });
11566             
11567             this.toolTip.render(Roo.get(document.body));
11568
11569             this.toolTip.el.enableDisplayMode("block");
11570             
11571             Roo.get(document.body).on('click', function(){
11572                 this.unmask();
11573             }, this);
11574             
11575             Roo.get(document.body).on('touchstart', function(){
11576                 this.unmask();
11577             }, this);
11578             
11579             this.isApplied = true
11580         },
11581         
11582         mask : function(form, target)
11583         {
11584             this.form = form;
11585             
11586             this.target = target;
11587             
11588             if(!this.form.errorMask || !target.el){
11589                 return;
11590             }
11591             
11592             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11593             
11594             Roo.log(scrollable);
11595             
11596             var ot = this.target.el.calcOffsetsTo(scrollable);
11597             
11598             var scrollTo = ot[1] - this.form.maskOffset;
11599             
11600             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11601             
11602             scrollable.scrollTo('top', scrollTo);
11603             
11604             var box = this.target.el.getBox();
11605             Roo.log(box);
11606             var zIndex = Roo.bootstrap.Modal.zIndex++;
11607
11608             
11609             this.maskEl.top.setStyle('position', 'absolute');
11610             this.maskEl.top.setStyle('z-index', zIndex);
11611             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11612             this.maskEl.top.setLeft(0);
11613             this.maskEl.top.setTop(0);
11614             this.maskEl.top.show();
11615             
11616             this.maskEl.left.setStyle('position', 'absolute');
11617             this.maskEl.left.setStyle('z-index', zIndex);
11618             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11619             this.maskEl.left.setLeft(0);
11620             this.maskEl.left.setTop(box.y - this.padding);
11621             this.maskEl.left.show();
11622
11623             this.maskEl.bottom.setStyle('position', 'absolute');
11624             this.maskEl.bottom.setStyle('z-index', zIndex);
11625             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11626             this.maskEl.bottom.setLeft(0);
11627             this.maskEl.bottom.setTop(box.bottom + this.padding);
11628             this.maskEl.bottom.show();
11629
11630             this.maskEl.right.setStyle('position', 'absolute');
11631             this.maskEl.right.setStyle('z-index', zIndex);
11632             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11633             this.maskEl.right.setLeft(box.right + this.padding);
11634             this.maskEl.right.setTop(box.y - this.padding);
11635             this.maskEl.right.show();
11636
11637             this.toolTip.bindEl = this.target.el;
11638
11639             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11640
11641             var tip = this.target.blankText;
11642
11643             if(this.target.getValue() !== '' ) {
11644                 
11645                 if (this.target.invalidText.length) {
11646                     tip = this.target.invalidText;
11647                 } else if (this.target.regexText.length){
11648                     tip = this.target.regexText;
11649                 }
11650             }
11651
11652             this.toolTip.show(tip);
11653
11654             this.intervalID = window.setInterval(function() {
11655                 Roo.bootstrap.Form.popover.unmask();
11656             }, 10000);
11657
11658             window.onwheel = function(){ return false;};
11659             
11660             (function(){ this.isMasked = true; }).defer(500, this);
11661             
11662         },
11663         
11664         unmask : function()
11665         {
11666             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11667                 return;
11668             }
11669             
11670             this.maskEl.top.setStyle('position', 'absolute');
11671             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11672             this.maskEl.top.hide();
11673
11674             this.maskEl.left.setStyle('position', 'absolute');
11675             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11676             this.maskEl.left.hide();
11677
11678             this.maskEl.bottom.setStyle('position', 'absolute');
11679             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11680             this.maskEl.bottom.hide();
11681
11682             this.maskEl.right.setStyle('position', 'absolute');
11683             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11684             this.maskEl.right.hide();
11685             
11686             this.toolTip.hide();
11687             
11688             this.toolTip.el.hide();
11689             
11690             window.onwheel = function(){ return true;};
11691             
11692             if(this.intervalID){
11693                 window.clearInterval(this.intervalID);
11694                 this.intervalID = false;
11695             }
11696             
11697             this.isMasked = false;
11698             
11699         }
11700         
11701     }
11702     
11703 });
11704
11705 /*
11706  * Based on:
11707  * Ext JS Library 1.1.1
11708  * Copyright(c) 2006-2007, Ext JS, LLC.
11709  *
11710  * Originally Released Under LGPL - original licence link has changed is not relivant.
11711  *
11712  * Fork - LGPL
11713  * <script type="text/javascript">
11714  */
11715 /**
11716  * @class Roo.form.VTypes
11717  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11718  * @singleton
11719  */
11720 Roo.form.VTypes = function(){
11721     // closure these in so they are only created once.
11722     var alpha = /^[a-zA-Z_]+$/;
11723     var alphanum = /^[a-zA-Z0-9_]+$/;
11724     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11725     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11726
11727     // All these messages and functions are configurable
11728     return {
11729         /**
11730          * The function used to validate email addresses
11731          * @param {String} value The email address
11732          */
11733         'email' : function(v){
11734             return email.test(v);
11735         },
11736         /**
11737          * The error text to display when the email validation function returns false
11738          * @type String
11739          */
11740         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11741         /**
11742          * The keystroke filter mask to be applied on email input
11743          * @type RegExp
11744          */
11745         'emailMask' : /[a-z0-9_\.\-@]/i,
11746
11747         /**
11748          * The function used to validate URLs
11749          * @param {String} value The URL
11750          */
11751         'url' : function(v){
11752             return url.test(v);
11753         },
11754         /**
11755          * The error text to display when the url validation function returns false
11756          * @type String
11757          */
11758         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11759         
11760         /**
11761          * The function used to validate alpha values
11762          * @param {String} value The value
11763          */
11764         'alpha' : function(v){
11765             return alpha.test(v);
11766         },
11767         /**
11768          * The error text to display when the alpha validation function returns false
11769          * @type String
11770          */
11771         'alphaText' : 'This field should only contain letters and _',
11772         /**
11773          * The keystroke filter mask to be applied on alpha input
11774          * @type RegExp
11775          */
11776         'alphaMask' : /[a-z_]/i,
11777
11778         /**
11779          * The function used to validate alphanumeric values
11780          * @param {String} value The value
11781          */
11782         'alphanum' : function(v){
11783             return alphanum.test(v);
11784         },
11785         /**
11786          * The error text to display when the alphanumeric validation function returns false
11787          * @type String
11788          */
11789         'alphanumText' : 'This field should only contain letters, numbers and _',
11790         /**
11791          * The keystroke filter mask to be applied on alphanumeric input
11792          * @type RegExp
11793          */
11794         'alphanumMask' : /[a-z0-9_]/i
11795     };
11796 }();/*
11797  * - LGPL
11798  *
11799  * Input
11800  * 
11801  */
11802
11803 /**
11804  * @class Roo.bootstrap.Input
11805  * @extends Roo.bootstrap.Component
11806  * Bootstrap Input class
11807  * @cfg {Boolean} disabled is it disabled
11808  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11809  * @cfg {String} name name of the input
11810  * @cfg {string} fieldLabel - the label associated
11811  * @cfg {string} placeholder - placeholder to put in text.
11812  * @cfg {string}  before - input group add on before
11813  * @cfg {string} after - input group add on after
11814  * @cfg {string} size - (lg|sm) or leave empty..
11815  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11816  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11817  * @cfg {Number} md colspan out of 12 for computer-sized screens
11818  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11819  * @cfg {string} value default value of the input
11820  * @cfg {Number} labelWidth set the width of label 
11821  * @cfg {Number} labellg set the width of label (1-12)
11822  * @cfg {Number} labelmd set the width of label (1-12)
11823  * @cfg {Number} labelsm set the width of label (1-12)
11824  * @cfg {Number} labelxs set the width of label (1-12)
11825  * @cfg {String} labelAlign (top|left)
11826  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11827  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11828  * @cfg {String} indicatorpos (left|right) default left
11829  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11830  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11831  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11832
11833  * @cfg {String} align (left|center|right) Default left
11834  * @cfg {Boolean} forceFeedback (true|false) Default false
11835  * 
11836  * @constructor
11837  * Create a new Input
11838  * @param {Object} config The config object
11839  */
11840
11841 Roo.bootstrap.Input = function(config){
11842     
11843     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11844     
11845     this.addEvents({
11846         /**
11847          * @event focus
11848          * Fires when this field receives input focus.
11849          * @param {Roo.form.Field} this
11850          */
11851         focus : true,
11852         /**
11853          * @event blur
11854          * Fires when this field loses input focus.
11855          * @param {Roo.form.Field} this
11856          */
11857         blur : true,
11858         /**
11859          * @event specialkey
11860          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11861          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11862          * @param {Roo.form.Field} this
11863          * @param {Roo.EventObject} e The event object
11864          */
11865         specialkey : true,
11866         /**
11867          * @event change
11868          * Fires just before the field blurs if the field value has changed.
11869          * @param {Roo.form.Field} this
11870          * @param {Mixed} newValue The new value
11871          * @param {Mixed} oldValue The original value
11872          */
11873         change : true,
11874         /**
11875          * @event invalid
11876          * Fires after the field has been marked as invalid.
11877          * @param {Roo.form.Field} this
11878          * @param {String} msg The validation message
11879          */
11880         invalid : true,
11881         /**
11882          * @event valid
11883          * Fires after the field has been validated with no errors.
11884          * @param {Roo.form.Field} this
11885          */
11886         valid : true,
11887          /**
11888          * @event keyup
11889          * Fires after the key up
11890          * @param {Roo.form.Field} this
11891          * @param {Roo.EventObject}  e The event Object
11892          */
11893         keyup : true,
11894         /**
11895          * @event paste
11896          * Fires after the user pastes into input
11897          * @param {Roo.form.Field} this
11898          * @param {Roo.EventObject}  e The event Object
11899          */
11900         paste : true
11901     });
11902 };
11903
11904 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11905      /**
11906      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11907       automatic validation (defaults to "keyup").
11908      */
11909     validationEvent : "keyup",
11910      /**
11911      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11912      */
11913     validateOnBlur : true,
11914     /**
11915      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11916      */
11917     validationDelay : 250,
11918      /**
11919      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11920      */
11921     focusClass : "x-form-focus",  // not needed???
11922     
11923        
11924     /**
11925      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11926      */
11927     invalidClass : "has-warning",
11928     
11929     /**
11930      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11931      */
11932     validClass : "has-success",
11933     
11934     /**
11935      * @cfg {Boolean} hasFeedback (true|false) default true
11936      */
11937     hasFeedback : true,
11938     
11939     /**
11940      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11941      */
11942     invalidFeedbackClass : "glyphicon-warning-sign",
11943     
11944     /**
11945      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11946      */
11947     validFeedbackClass : "glyphicon-ok",
11948     
11949     /**
11950      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11951      */
11952     selectOnFocus : false,
11953     
11954      /**
11955      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11956      */
11957     maskRe : null,
11958        /**
11959      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11960      */
11961     vtype : null,
11962     
11963       /**
11964      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11965      */
11966     disableKeyFilter : false,
11967     
11968        /**
11969      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11970      */
11971     disabled : false,
11972      /**
11973      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11974      */
11975     allowBlank : true,
11976     /**
11977      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11978      */
11979     blankText : "Please complete this mandatory field",
11980     
11981      /**
11982      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11983      */
11984     minLength : 0,
11985     /**
11986      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11987      */
11988     maxLength : Number.MAX_VALUE,
11989     /**
11990      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11991      */
11992     minLengthText : "The minimum length for this field is {0}",
11993     /**
11994      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11995      */
11996     maxLengthText : "The maximum length for this field is {0}",
11997   
11998     
11999     /**
12000      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12001      * If available, this function will be called only after the basic validators all return true, and will be passed the
12002      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12003      */
12004     validator : null,
12005     /**
12006      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12007      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12008      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12009      */
12010     regex : null,
12011     /**
12012      * @cfg {String} regexText -- Depricated - use Invalid Text
12013      */
12014     regexText : "",
12015     
12016     /**
12017      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12018      */
12019     invalidText : "",
12020     
12021     
12022     
12023     autocomplete: false,
12024     
12025     
12026     fieldLabel : '',
12027     inputType : 'text',
12028     
12029     name : false,
12030     placeholder: false,
12031     before : false,
12032     after : false,
12033     size : false,
12034     hasFocus : false,
12035     preventMark: false,
12036     isFormField : true,
12037     value : '',
12038     labelWidth : 2,
12039     labelAlign : false,
12040     readOnly : false,
12041     align : false,
12042     formatedValue : false,
12043     forceFeedback : false,
12044     
12045     indicatorpos : 'left',
12046     
12047     labellg : 0,
12048     labelmd : 0,
12049     labelsm : 0,
12050     labelxs : 0,
12051     
12052     capture : '',
12053     accept : '',
12054     
12055     parentLabelAlign : function()
12056     {
12057         var parent = this;
12058         while (parent.parent()) {
12059             parent = parent.parent();
12060             if (typeof(parent.labelAlign) !='undefined') {
12061                 return parent.labelAlign;
12062             }
12063         }
12064         return 'left';
12065         
12066     },
12067     
12068     getAutoCreate : function()
12069     {
12070         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12071         
12072         var id = Roo.id();
12073         
12074         var cfg = {};
12075         
12076         if(this.inputType != 'hidden'){
12077             cfg.cls = 'form-group' //input-group
12078         }
12079         
12080         var input =  {
12081             tag: 'input',
12082             id : id,
12083             type : this.inputType,
12084             value : this.value,
12085             cls : 'form-control',
12086             placeholder : this.placeholder || '',
12087             autocomplete : this.autocomplete || 'new-password'
12088         };
12089         if (this.inputType == 'file') {
12090             input.style = 'overflow:hidden'; // why not in CSS?
12091         }
12092         
12093         if(this.capture.length){
12094             input.capture = this.capture;
12095         }
12096         
12097         if(this.accept.length){
12098             input.accept = this.accept + "/*";
12099         }
12100         
12101         if(this.align){
12102             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12103         }
12104         
12105         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12106             input.maxLength = this.maxLength;
12107         }
12108         
12109         if (this.disabled) {
12110             input.disabled=true;
12111         }
12112         
12113         if (this.readOnly) {
12114             input.readonly=true;
12115         }
12116         
12117         if (this.name) {
12118             input.name = this.name;
12119         }
12120         
12121         if (this.size) {
12122             input.cls += ' input-' + this.size;
12123         }
12124         
12125         var settings=this;
12126         ['xs','sm','md','lg'].map(function(size){
12127             if (settings[size]) {
12128                 cfg.cls += ' col-' + size + '-' + settings[size];
12129             }
12130         });
12131         
12132         var inputblock = input;
12133         
12134         var feedback = {
12135             tag: 'span',
12136             cls: 'glyphicon form-control-feedback'
12137         };
12138             
12139         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12140             
12141             inputblock = {
12142                 cls : 'has-feedback',
12143                 cn :  [
12144                     input,
12145                     feedback
12146                 ] 
12147             };  
12148         }
12149         
12150         if (this.before || this.after) {
12151             
12152             inputblock = {
12153                 cls : 'input-group',
12154                 cn :  [] 
12155             };
12156             
12157             if (this.before && typeof(this.before) == 'string') {
12158                 
12159                 inputblock.cn.push({
12160                     tag :'span',
12161                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12162                     html : this.before
12163                 });
12164             }
12165             if (this.before && typeof(this.before) == 'object') {
12166                 this.before = Roo.factory(this.before);
12167                 
12168                 inputblock.cn.push({
12169                     tag :'span',
12170                     cls : 'roo-input-before input-group-prepend   input-group-' +
12171                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12172                 });
12173             }
12174             
12175             inputblock.cn.push(input);
12176             
12177             if (this.after && typeof(this.after) == 'string') {
12178                 inputblock.cn.push({
12179                     tag :'span',
12180                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12181                     html : this.after
12182                 });
12183             }
12184             if (this.after && typeof(this.after) == 'object') {
12185                 this.after = Roo.factory(this.after);
12186                 
12187                 inputblock.cn.push({
12188                     tag :'span',
12189                     cls : 'roo-input-after input-group-append  input-group-' +
12190                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12191                 });
12192             }
12193             
12194             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12195                 inputblock.cls += ' has-feedback';
12196                 inputblock.cn.push(feedback);
12197             }
12198         };
12199         var indicator = {
12200             tag : 'i',
12201             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12202             tooltip : 'This field is required'
12203         };
12204         if (this.allowBlank ) {
12205             indicator.style = this.allowBlank ? ' display:none' : '';
12206         }
12207         if (align ==='left' && this.fieldLabel.length) {
12208             
12209             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12210             
12211             cfg.cn = [
12212                 indicator,
12213                 {
12214                     tag: 'label',
12215                     'for' :  id,
12216                     cls : 'control-label col-form-label',
12217                     html : this.fieldLabel
12218
12219                 },
12220                 {
12221                     cls : "", 
12222                     cn: [
12223                         inputblock
12224                     ]
12225                 }
12226             ];
12227             
12228             var labelCfg = cfg.cn[1];
12229             var contentCfg = cfg.cn[2];
12230             
12231             if(this.indicatorpos == 'right'){
12232                 cfg.cn = [
12233                     {
12234                         tag: 'label',
12235                         'for' :  id,
12236                         cls : 'control-label col-form-label',
12237                         cn : [
12238                             {
12239                                 tag : 'span',
12240                                 html : this.fieldLabel
12241                             },
12242                             indicator
12243                         ]
12244                     },
12245                     {
12246                         cls : "",
12247                         cn: [
12248                             inputblock
12249                         ]
12250                     }
12251
12252                 ];
12253                 
12254                 labelCfg = cfg.cn[0];
12255                 contentCfg = cfg.cn[1];
12256             
12257             }
12258             
12259             if(this.labelWidth > 12){
12260                 labelCfg.style = "width: " + this.labelWidth + 'px';
12261             }
12262             
12263             if(this.labelWidth < 13 && this.labelmd == 0){
12264                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12265             }
12266             
12267             if(this.labellg > 0){
12268                 labelCfg.cls += ' col-lg-' + this.labellg;
12269                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12270             }
12271             
12272             if(this.labelmd > 0){
12273                 labelCfg.cls += ' col-md-' + this.labelmd;
12274                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12275             }
12276             
12277             if(this.labelsm > 0){
12278                 labelCfg.cls += ' col-sm-' + this.labelsm;
12279                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12280             }
12281             
12282             if(this.labelxs > 0){
12283                 labelCfg.cls += ' col-xs-' + this.labelxs;
12284                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12285             }
12286             
12287             
12288         } else if ( this.fieldLabel.length) {
12289                 
12290             
12291             
12292             cfg.cn = [
12293                 {
12294                     tag : 'i',
12295                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12296                     tooltip : 'This field is required',
12297                     style : this.allowBlank ? ' display:none' : '' 
12298                 },
12299                 {
12300                     tag: 'label',
12301                    //cls : 'input-group-addon',
12302                     html : this.fieldLabel
12303
12304                 },
12305
12306                inputblock
12307
12308            ];
12309            
12310            if(this.indicatorpos == 'right'){
12311        
12312                 cfg.cn = [
12313                     {
12314                         tag: 'label',
12315                        //cls : 'input-group-addon',
12316                         html : this.fieldLabel
12317
12318                     },
12319                     {
12320                         tag : 'i',
12321                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12322                         tooltip : 'This field is required',
12323                         style : this.allowBlank ? ' display:none' : '' 
12324                     },
12325
12326                    inputblock
12327
12328                ];
12329
12330             }
12331
12332         } else {
12333             
12334             cfg.cn = [
12335
12336                     inputblock
12337
12338             ];
12339                 
12340                 
12341         };
12342         
12343         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12344            cfg.cls += ' navbar-form';
12345         }
12346         
12347         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12348             // on BS4 we do this only if not form 
12349             cfg.cls += ' navbar-form';
12350             cfg.tag = 'li';
12351         }
12352         
12353         return cfg;
12354         
12355     },
12356     /**
12357      * return the real input element.
12358      */
12359     inputEl: function ()
12360     {
12361         return this.el.select('input.form-control',true).first();
12362     },
12363     
12364     tooltipEl : function()
12365     {
12366         return this.inputEl();
12367     },
12368     
12369     indicatorEl : function()
12370     {
12371         if (Roo.bootstrap.version == 4) {
12372             return false; // not enabled in v4 yet.
12373         }
12374         
12375         var indicator = this.el.select('i.roo-required-indicator',true).first();
12376         
12377         if(!indicator){
12378             return false;
12379         }
12380         
12381         return indicator;
12382         
12383     },
12384     
12385     setDisabled : function(v)
12386     {
12387         var i  = this.inputEl().dom;
12388         if (!v) {
12389             i.removeAttribute('disabled');
12390             return;
12391             
12392         }
12393         i.setAttribute('disabled','true');
12394     },
12395     initEvents : function()
12396     {
12397           
12398         this.inputEl().on("keydown" , this.fireKey,  this);
12399         this.inputEl().on("focus", this.onFocus,  this);
12400         this.inputEl().on("blur", this.onBlur,  this);
12401         
12402         this.inputEl().relayEvent('keyup', this);
12403         this.inputEl().relayEvent('paste', this);
12404         
12405         this.indicator = this.indicatorEl();
12406         
12407         if(this.indicator){
12408             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12409         }
12410  
12411         // reference to original value for reset
12412         this.originalValue = this.getValue();
12413         //Roo.form.TextField.superclass.initEvents.call(this);
12414         if(this.validationEvent == 'keyup'){
12415             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12416             this.inputEl().on('keyup', this.filterValidation, this);
12417         }
12418         else if(this.validationEvent !== false){
12419             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12420         }
12421         
12422         if(this.selectOnFocus){
12423             this.on("focus", this.preFocus, this);
12424             
12425         }
12426         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12427             this.inputEl().on("keypress", this.filterKeys, this);
12428         } else {
12429             this.inputEl().relayEvent('keypress', this);
12430         }
12431        /* if(this.grow){
12432             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12433             this.el.on("click", this.autoSize,  this);
12434         }
12435         */
12436         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12437             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12438         }
12439         
12440         if (typeof(this.before) == 'object') {
12441             this.before.render(this.el.select('.roo-input-before',true).first());
12442         }
12443         if (typeof(this.after) == 'object') {
12444             this.after.render(this.el.select('.roo-input-after',true).first());
12445         }
12446         
12447         this.inputEl().on('change', this.onChange, this);
12448         
12449     },
12450     filterValidation : function(e){
12451         if(!e.isNavKeyPress()){
12452             this.validationTask.delay(this.validationDelay);
12453         }
12454     },
12455      /**
12456      * Validates the field value
12457      * @return {Boolean} True if the value is valid, else false
12458      */
12459     validate : function(){
12460         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12461         if(this.disabled || this.validateValue(this.getRawValue())){
12462             this.markValid();
12463             return true;
12464         }
12465         
12466         this.markInvalid();
12467         return false;
12468     },
12469     
12470     
12471     /**
12472      * Validates a value according to the field's validation rules and marks the field as invalid
12473      * if the validation fails
12474      * @param {Mixed} value The value to validate
12475      * @return {Boolean} True if the value is valid, else false
12476      */
12477     validateValue : function(value)
12478     {
12479         if(this.getVisibilityEl().hasClass('hidden')){
12480             return true;
12481         }
12482         
12483         if(value.length < 1)  { // if it's blank
12484             if(this.allowBlank){
12485                 return true;
12486             }
12487             return false;
12488         }
12489         
12490         if(value.length < this.minLength){
12491             return false;
12492         }
12493         if(value.length > this.maxLength){
12494             return false;
12495         }
12496         if(this.vtype){
12497             var vt = Roo.form.VTypes;
12498             if(!vt[this.vtype](value, this)){
12499                 return false;
12500             }
12501         }
12502         if(typeof this.validator == "function"){
12503             var msg = this.validator(value);
12504             if(msg !== true){
12505                 return false;
12506             }
12507             if (typeof(msg) == 'string') {
12508                 this.invalidText = msg;
12509             }
12510         }
12511         
12512         if(this.regex && !this.regex.test(value)){
12513             return false;
12514         }
12515         
12516         return true;
12517     },
12518     
12519      // private
12520     fireKey : function(e){
12521         //Roo.log('field ' + e.getKey());
12522         if(e.isNavKeyPress()){
12523             this.fireEvent("specialkey", this, e);
12524         }
12525     },
12526     focus : function (selectText){
12527         if(this.rendered){
12528             this.inputEl().focus();
12529             if(selectText === true){
12530                 this.inputEl().dom.select();
12531             }
12532         }
12533         return this;
12534     } ,
12535     
12536     onFocus : function(){
12537         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12538            // this.el.addClass(this.focusClass);
12539         }
12540         if(!this.hasFocus){
12541             this.hasFocus = true;
12542             this.startValue = this.getValue();
12543             this.fireEvent("focus", this);
12544         }
12545     },
12546     
12547     beforeBlur : Roo.emptyFn,
12548
12549     
12550     // private
12551     onBlur : function(){
12552         this.beforeBlur();
12553         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12554             //this.el.removeClass(this.focusClass);
12555         }
12556         this.hasFocus = false;
12557         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12558             this.validate();
12559         }
12560         var v = this.getValue();
12561         if(String(v) !== String(this.startValue)){
12562             this.fireEvent('change', this, v, this.startValue);
12563         }
12564         this.fireEvent("blur", this);
12565     },
12566     
12567     onChange : function(e)
12568     {
12569         var v = this.getValue();
12570         if(String(v) !== String(this.startValue)){
12571             this.fireEvent('change', this, v, this.startValue);
12572         }
12573         
12574     },
12575     
12576     /**
12577      * Resets the current field value to the originally loaded value and clears any validation messages
12578      */
12579     reset : function(){
12580         this.setValue(this.originalValue);
12581         this.validate();
12582     },
12583      /**
12584      * Returns the name of the field
12585      * @return {Mixed} name The name field
12586      */
12587     getName: function(){
12588         return this.name;
12589     },
12590      /**
12591      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12592      * @return {Mixed} value The field value
12593      */
12594     getValue : function(){
12595         
12596         var v = this.inputEl().getValue();
12597         
12598         return v;
12599     },
12600     /**
12601      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12602      * @return {Mixed} value The field value
12603      */
12604     getRawValue : function(){
12605         var v = this.inputEl().getValue();
12606         
12607         return v;
12608     },
12609     
12610     /**
12611      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12612      * @param {Mixed} value The value to set
12613      */
12614     setRawValue : function(v){
12615         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12616     },
12617     
12618     selectText : function(start, end){
12619         var v = this.getRawValue();
12620         if(v.length > 0){
12621             start = start === undefined ? 0 : start;
12622             end = end === undefined ? v.length : end;
12623             var d = this.inputEl().dom;
12624             if(d.setSelectionRange){
12625                 d.setSelectionRange(start, end);
12626             }else if(d.createTextRange){
12627                 var range = d.createTextRange();
12628                 range.moveStart("character", start);
12629                 range.moveEnd("character", v.length-end);
12630                 range.select();
12631             }
12632         }
12633     },
12634     
12635     /**
12636      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12637      * @param {Mixed} value The value to set
12638      */
12639     setValue : function(v){
12640         this.value = v;
12641         if(this.rendered){
12642             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12643             this.validate();
12644         }
12645     },
12646     
12647     /*
12648     processValue : function(value){
12649         if(this.stripCharsRe){
12650             var newValue = value.replace(this.stripCharsRe, '');
12651             if(newValue !== value){
12652                 this.setRawValue(newValue);
12653                 return newValue;
12654             }
12655         }
12656         return value;
12657     },
12658   */
12659     preFocus : function(){
12660         
12661         if(this.selectOnFocus){
12662             this.inputEl().dom.select();
12663         }
12664     },
12665     filterKeys : function(e){
12666         var k = e.getKey();
12667         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12668             return;
12669         }
12670         var c = e.getCharCode(), cc = String.fromCharCode(c);
12671         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12672             return;
12673         }
12674         if(!this.maskRe.test(cc)){
12675             e.stopEvent();
12676         }
12677     },
12678      /**
12679      * Clear any invalid styles/messages for this field
12680      */
12681     clearInvalid : function(){
12682         
12683         if(!this.el || this.preventMark){ // not rendered
12684             return;
12685         }
12686         
12687         
12688         this.el.removeClass([this.invalidClass, 'is-invalid']);
12689         
12690         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12691             
12692             var feedback = this.el.select('.form-control-feedback', true).first();
12693             
12694             if(feedback){
12695                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12696             }
12697             
12698         }
12699         
12700         if(this.indicator){
12701             this.indicator.removeClass('visible');
12702             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12703         }
12704         
12705         this.fireEvent('valid', this);
12706     },
12707     
12708      /**
12709      * Mark this field as valid
12710      */
12711     markValid : function()
12712     {
12713         if(!this.el  || this.preventMark){ // not rendered...
12714             return;
12715         }
12716         
12717         this.el.removeClass([this.invalidClass, this.validClass]);
12718         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12719
12720         var feedback = this.el.select('.form-control-feedback', true).first();
12721             
12722         if(feedback){
12723             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12724         }
12725         
12726         if(this.indicator){
12727             this.indicator.removeClass('visible');
12728             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12729         }
12730         
12731         if(this.disabled){
12732             return;
12733         }
12734         
12735            
12736         if(this.allowBlank && !this.getRawValue().length){
12737             return;
12738         }
12739         if (Roo.bootstrap.version == 3) {
12740             this.el.addClass(this.validClass);
12741         } else {
12742             this.inputEl().addClass('is-valid');
12743         }
12744
12745         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12746             
12747             var feedback = this.el.select('.form-control-feedback', true).first();
12748             
12749             if(feedback){
12750                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12751                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12752             }
12753             
12754         }
12755         
12756         this.fireEvent('valid', this);
12757     },
12758     
12759      /**
12760      * Mark this field as invalid
12761      * @param {String} msg The validation message
12762      */
12763     markInvalid : function(msg)
12764     {
12765         if(!this.el  || this.preventMark){ // not rendered
12766             return;
12767         }
12768         
12769         this.el.removeClass([this.invalidClass, this.validClass]);
12770         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12771         
12772         var feedback = this.el.select('.form-control-feedback', true).first();
12773             
12774         if(feedback){
12775             this.el.select('.form-control-feedback', true).first().removeClass(
12776                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12777         }
12778
12779         if(this.disabled){
12780             return;
12781         }
12782         
12783         if(this.allowBlank && !this.getRawValue().length){
12784             return;
12785         }
12786         
12787         if(this.indicator){
12788             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12789             this.indicator.addClass('visible');
12790         }
12791         if (Roo.bootstrap.version == 3) {
12792             this.el.addClass(this.invalidClass);
12793         } else {
12794             this.inputEl().addClass('is-invalid');
12795         }
12796         
12797         
12798         
12799         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12800             
12801             var feedback = this.el.select('.form-control-feedback', true).first();
12802             
12803             if(feedback){
12804                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12805                 
12806                 if(this.getValue().length || this.forceFeedback){
12807                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12808                 }
12809                 
12810             }
12811             
12812         }
12813         
12814         this.fireEvent('invalid', this, msg);
12815     },
12816     // private
12817     SafariOnKeyDown : function(event)
12818     {
12819         // this is a workaround for a password hang bug on chrome/ webkit.
12820         if (this.inputEl().dom.type != 'password') {
12821             return;
12822         }
12823         
12824         var isSelectAll = false;
12825         
12826         if(this.inputEl().dom.selectionEnd > 0){
12827             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12828         }
12829         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12830             event.preventDefault();
12831             this.setValue('');
12832             return;
12833         }
12834         
12835         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12836             
12837             event.preventDefault();
12838             // this is very hacky as keydown always get's upper case.
12839             //
12840             var cc = String.fromCharCode(event.getCharCode());
12841             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12842             
12843         }
12844     },
12845     adjustWidth : function(tag, w){
12846         tag = tag.toLowerCase();
12847         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12848             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12849                 if(tag == 'input'){
12850                     return w + 2;
12851                 }
12852                 if(tag == 'textarea'){
12853                     return w-2;
12854                 }
12855             }else if(Roo.isOpera){
12856                 if(tag == 'input'){
12857                     return w + 2;
12858                 }
12859                 if(tag == 'textarea'){
12860                     return w-2;
12861                 }
12862             }
12863         }
12864         return w;
12865     },
12866     
12867     setFieldLabel : function(v)
12868     {
12869         if(!this.rendered){
12870             return;
12871         }
12872         
12873         if(this.indicatorEl()){
12874             var ar = this.el.select('label > span',true);
12875             
12876             if (ar.elements.length) {
12877                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12878                 this.fieldLabel = v;
12879                 return;
12880             }
12881             
12882             var br = this.el.select('label',true);
12883             
12884             if(br.elements.length) {
12885                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12886                 this.fieldLabel = v;
12887                 return;
12888             }
12889             
12890             Roo.log('Cannot Found any of label > span || label in input');
12891             return;
12892         }
12893         
12894         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12895         this.fieldLabel = v;
12896         
12897         
12898     }
12899 });
12900
12901  
12902 /*
12903  * - LGPL
12904  *
12905  * Input
12906  * 
12907  */
12908
12909 /**
12910  * @class Roo.bootstrap.TextArea
12911  * @extends Roo.bootstrap.Input
12912  * Bootstrap TextArea class
12913  * @cfg {Number} cols Specifies the visible width of a text area
12914  * @cfg {Number} rows Specifies the visible number of lines in a text area
12915  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12916  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12917  * @cfg {string} html text
12918  * 
12919  * @constructor
12920  * Create a new TextArea
12921  * @param {Object} config The config object
12922  */
12923
12924 Roo.bootstrap.TextArea = function(config){
12925     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12926    
12927 };
12928
12929 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12930      
12931     cols : false,
12932     rows : 5,
12933     readOnly : false,
12934     warp : 'soft',
12935     resize : false,
12936     value: false,
12937     html: false,
12938     
12939     getAutoCreate : function(){
12940         
12941         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12942         
12943         var id = Roo.id();
12944         
12945         var cfg = {};
12946         
12947         if(this.inputType != 'hidden'){
12948             cfg.cls = 'form-group' //input-group
12949         }
12950         
12951         var input =  {
12952             tag: 'textarea',
12953             id : id,
12954             warp : this.warp,
12955             rows : this.rows,
12956             value : this.value || '',
12957             html: this.html || '',
12958             cls : 'form-control',
12959             placeholder : this.placeholder || '' 
12960             
12961         };
12962         
12963         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12964             input.maxLength = this.maxLength;
12965         }
12966         
12967         if(this.resize){
12968             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12969         }
12970         
12971         if(this.cols){
12972             input.cols = this.cols;
12973         }
12974         
12975         if (this.readOnly) {
12976             input.readonly = true;
12977         }
12978         
12979         if (this.name) {
12980             input.name = this.name;
12981         }
12982         
12983         if (this.size) {
12984             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12985         }
12986         
12987         var settings=this;
12988         ['xs','sm','md','lg'].map(function(size){
12989             if (settings[size]) {
12990                 cfg.cls += ' col-' + size + '-' + settings[size];
12991             }
12992         });
12993         
12994         var inputblock = input;
12995         
12996         if(this.hasFeedback && !this.allowBlank){
12997             
12998             var feedback = {
12999                 tag: 'span',
13000                 cls: 'glyphicon form-control-feedback'
13001             };
13002
13003             inputblock = {
13004                 cls : 'has-feedback',
13005                 cn :  [
13006                     input,
13007                     feedback
13008                 ] 
13009             };  
13010         }
13011         
13012         
13013         if (this.before || this.after) {
13014             
13015             inputblock = {
13016                 cls : 'input-group',
13017                 cn :  [] 
13018             };
13019             if (this.before) {
13020                 inputblock.cn.push({
13021                     tag :'span',
13022                     cls : 'input-group-addon',
13023                     html : this.before
13024                 });
13025             }
13026             
13027             inputblock.cn.push(input);
13028             
13029             if(this.hasFeedback && !this.allowBlank){
13030                 inputblock.cls += ' has-feedback';
13031                 inputblock.cn.push(feedback);
13032             }
13033             
13034             if (this.after) {
13035                 inputblock.cn.push({
13036                     tag :'span',
13037                     cls : 'input-group-addon',
13038                     html : this.after
13039                 });
13040             }
13041             
13042         }
13043         
13044         if (align ==='left' && this.fieldLabel.length) {
13045             cfg.cn = [
13046                 {
13047                     tag: 'label',
13048                     'for' :  id,
13049                     cls : 'control-label',
13050                     html : this.fieldLabel
13051                 },
13052                 {
13053                     cls : "",
13054                     cn: [
13055                         inputblock
13056                     ]
13057                 }
13058
13059             ];
13060             
13061             if(this.labelWidth > 12){
13062                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13063             }
13064
13065             if(this.labelWidth < 13 && this.labelmd == 0){
13066                 this.labelmd = this.labelWidth;
13067             }
13068
13069             if(this.labellg > 0){
13070                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13071                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13072             }
13073
13074             if(this.labelmd > 0){
13075                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13076                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13077             }
13078
13079             if(this.labelsm > 0){
13080                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13081                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13082             }
13083
13084             if(this.labelxs > 0){
13085                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13086                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13087             }
13088             
13089         } else if ( this.fieldLabel.length) {
13090             cfg.cn = [
13091
13092                {
13093                    tag: 'label',
13094                    //cls : 'input-group-addon',
13095                    html : this.fieldLabel
13096
13097                },
13098
13099                inputblock
13100
13101            ];
13102
13103         } else {
13104
13105             cfg.cn = [
13106
13107                 inputblock
13108
13109             ];
13110                 
13111         }
13112         
13113         if (this.disabled) {
13114             input.disabled=true;
13115         }
13116         
13117         return cfg;
13118         
13119     },
13120     /**
13121      * return the real textarea element.
13122      */
13123     inputEl: function ()
13124     {
13125         return this.el.select('textarea.form-control',true).first();
13126     },
13127     
13128     /**
13129      * Clear any invalid styles/messages for this field
13130      */
13131     clearInvalid : function()
13132     {
13133         
13134         if(!this.el || this.preventMark){ // not rendered
13135             return;
13136         }
13137         
13138         var label = this.el.select('label', true).first();
13139         var icon = this.el.select('i.fa-star', true).first();
13140         
13141         if(label && icon){
13142             icon.remove();
13143         }
13144         this.el.removeClass( this.validClass);
13145         this.inputEl().removeClass('is-invalid');
13146          
13147         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13148             
13149             var feedback = this.el.select('.form-control-feedback', true).first();
13150             
13151             if(feedback){
13152                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13153             }
13154             
13155         }
13156         
13157         this.fireEvent('valid', this);
13158     },
13159     
13160      /**
13161      * Mark this field as valid
13162      */
13163     markValid : function()
13164     {
13165         if(!this.el  || this.preventMark){ // not rendered
13166             return;
13167         }
13168         
13169         this.el.removeClass([this.invalidClass, this.validClass]);
13170         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13171         
13172         var feedback = this.el.select('.form-control-feedback', true).first();
13173             
13174         if(feedback){
13175             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13176         }
13177
13178         if(this.disabled || this.allowBlank){
13179             return;
13180         }
13181         
13182         var label = this.el.select('label', true).first();
13183         var icon = this.el.select('i.fa-star', true).first();
13184         
13185         if(label && icon){
13186             icon.remove();
13187         }
13188         if (Roo.bootstrap.version == 3) {
13189             this.el.addClass(this.validClass);
13190         } else {
13191             this.inputEl().addClass('is-valid');
13192         }
13193         
13194         
13195         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13196             
13197             var feedback = this.el.select('.form-control-feedback', true).first();
13198             
13199             if(feedback){
13200                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13201                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13202             }
13203             
13204         }
13205         
13206         this.fireEvent('valid', this);
13207     },
13208     
13209      /**
13210      * Mark this field as invalid
13211      * @param {String} msg The validation message
13212      */
13213     markInvalid : function(msg)
13214     {
13215         if(!this.el  || this.preventMark){ // not rendered
13216             return;
13217         }
13218         
13219         this.el.removeClass([this.invalidClass, this.validClass]);
13220         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13221         
13222         var feedback = this.el.select('.form-control-feedback', true).first();
13223             
13224         if(feedback){
13225             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13226         }
13227
13228         if(this.disabled || this.allowBlank){
13229             return;
13230         }
13231         
13232         var label = this.el.select('label', true).first();
13233         var icon = this.el.select('i.fa-star', true).first();
13234         
13235         if(!this.getValue().length && label && !icon){
13236             this.el.createChild({
13237                 tag : 'i',
13238                 cls : 'text-danger fa fa-lg fa-star',
13239                 tooltip : 'This field is required',
13240                 style : 'margin-right:5px;'
13241             }, label, true);
13242         }
13243         
13244         if (Roo.bootstrap.version == 3) {
13245             this.el.addClass(this.invalidClass);
13246         } else {
13247             this.inputEl().addClass('is-invalid');
13248         }
13249         
13250         // fixme ... this may be depricated need to test..
13251         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13252             
13253             var feedback = this.el.select('.form-control-feedback', true).first();
13254             
13255             if(feedback){
13256                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13257                 
13258                 if(this.getValue().length || this.forceFeedback){
13259                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13260                 }
13261                 
13262             }
13263             
13264         }
13265         
13266         this.fireEvent('invalid', this, msg);
13267     }
13268 });
13269
13270  
13271 /*
13272  * - LGPL
13273  *
13274  * trigger field - base class for combo..
13275  * 
13276  */
13277  
13278 /**
13279  * @class Roo.bootstrap.TriggerField
13280  * @extends Roo.bootstrap.Input
13281  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13282  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13283  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13284  * for which you can provide a custom implementation.  For example:
13285  * <pre><code>
13286 var trigger = new Roo.bootstrap.TriggerField();
13287 trigger.onTriggerClick = myTriggerFn;
13288 trigger.applyTo('my-field');
13289 </code></pre>
13290  *
13291  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13292  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13293  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13294  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13295  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13296
13297  * @constructor
13298  * Create a new TriggerField.
13299  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13300  * to the base TextField)
13301  */
13302 Roo.bootstrap.TriggerField = function(config){
13303     this.mimicing = false;
13304     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13305 };
13306
13307 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13308     /**
13309      * @cfg {String} triggerClass A CSS class to apply to the trigger
13310      */
13311      /**
13312      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13313      */
13314     hideTrigger:false,
13315
13316     /**
13317      * @cfg {Boolean} removable (true|false) special filter default false
13318      */
13319     removable : false,
13320     
13321     /** @cfg {Boolean} grow @hide */
13322     /** @cfg {Number} growMin @hide */
13323     /** @cfg {Number} growMax @hide */
13324
13325     /**
13326      * @hide 
13327      * @method
13328      */
13329     autoSize: Roo.emptyFn,
13330     // private
13331     monitorTab : true,
13332     // private
13333     deferHeight : true,
13334
13335     
13336     actionMode : 'wrap',
13337     
13338     caret : false,
13339     
13340     
13341     getAutoCreate : function(){
13342        
13343         var align = this.labelAlign || this.parentLabelAlign();
13344         
13345         var id = Roo.id();
13346         
13347         var cfg = {
13348             cls: 'form-group' //input-group
13349         };
13350         
13351         
13352         var input =  {
13353             tag: 'input',
13354             id : id,
13355             type : this.inputType,
13356             cls : 'form-control',
13357             autocomplete: 'new-password',
13358             placeholder : this.placeholder || '' 
13359             
13360         };
13361         if (this.name) {
13362             input.name = this.name;
13363         }
13364         if (this.size) {
13365             input.cls += ' input-' + this.size;
13366         }
13367         
13368         if (this.disabled) {
13369             input.disabled=true;
13370         }
13371         
13372         var inputblock = input;
13373         
13374         if(this.hasFeedback && !this.allowBlank){
13375             
13376             var feedback = {
13377                 tag: 'span',
13378                 cls: 'glyphicon form-control-feedback'
13379             };
13380             
13381             if(this.removable && !this.editable  ){
13382                 inputblock = {
13383                     cls : 'has-feedback',
13384                     cn :  [
13385                         inputblock,
13386                         {
13387                             tag: 'button',
13388                             html : 'x',
13389                             cls : 'roo-combo-removable-btn close'
13390                         },
13391                         feedback
13392                     ] 
13393                 };
13394             } else {
13395                 inputblock = {
13396                     cls : 'has-feedback',
13397                     cn :  [
13398                         inputblock,
13399                         feedback
13400                     ] 
13401                 };
13402             }
13403
13404         } else {
13405             if(this.removable && !this.editable ){
13406                 inputblock = {
13407                     cls : 'roo-removable',
13408                     cn :  [
13409                         inputblock,
13410                         {
13411                             tag: 'button',
13412                             html : 'x',
13413                             cls : 'roo-combo-removable-btn close'
13414                         }
13415                     ] 
13416                 };
13417             }
13418         }
13419         
13420         if (this.before || this.after) {
13421             
13422             inputblock = {
13423                 cls : 'input-group',
13424                 cn :  [] 
13425             };
13426             if (this.before) {
13427                 inputblock.cn.push({
13428                     tag :'span',
13429                     cls : 'input-group-addon input-group-prepend input-group-text',
13430                     html : this.before
13431                 });
13432             }
13433             
13434             inputblock.cn.push(input);
13435             
13436             if(this.hasFeedback && !this.allowBlank){
13437                 inputblock.cls += ' has-feedback';
13438                 inputblock.cn.push(feedback);
13439             }
13440             
13441             if (this.after) {
13442                 inputblock.cn.push({
13443                     tag :'span',
13444                     cls : 'input-group-addon input-group-append input-group-text',
13445                     html : this.after
13446                 });
13447             }
13448             
13449         };
13450         
13451       
13452         
13453         var ibwrap = inputblock;
13454         
13455         if(this.multiple){
13456             ibwrap = {
13457                 tag: 'ul',
13458                 cls: 'roo-select2-choices',
13459                 cn:[
13460                     {
13461                         tag: 'li',
13462                         cls: 'roo-select2-search-field',
13463                         cn: [
13464
13465                             inputblock
13466                         ]
13467                     }
13468                 ]
13469             };
13470                 
13471         }
13472         
13473         var combobox = {
13474             cls: 'roo-select2-container input-group',
13475             cn: [
13476                  {
13477                     tag: 'input',
13478                     type : 'hidden',
13479                     cls: 'form-hidden-field'
13480                 },
13481                 ibwrap
13482             ]
13483         };
13484         
13485         if(!this.multiple && this.showToggleBtn){
13486             
13487             var caret = {
13488                         tag: 'span',
13489                         cls: 'caret'
13490              };
13491             if (this.caret != false) {
13492                 caret = {
13493                      tag: 'i',
13494                      cls: 'fa fa-' + this.caret
13495                 };
13496                 
13497             }
13498             
13499             combobox.cn.push({
13500                 tag :'span',
13501                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13502                 cn : [
13503                     Roo.bootstrap.version == 3 ? caret : '',
13504                     {
13505                         tag: 'span',
13506                         cls: 'combobox-clear',
13507                         cn  : [
13508                             {
13509                                 tag : 'i',
13510                                 cls: 'icon-remove'
13511                             }
13512                         ]
13513                     }
13514                 ]
13515
13516             })
13517         }
13518         
13519         if(this.multiple){
13520             combobox.cls += ' roo-select2-container-multi';
13521         }
13522          var indicator = {
13523             tag : 'i',
13524             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13525             tooltip : 'This field is required'
13526         };
13527         if (Roo.bootstrap.version == 4) {
13528             indicator = {
13529                 tag : 'i',
13530                 style : 'display:none'
13531             };
13532         }
13533         
13534         
13535         if (align ==='left' && this.fieldLabel.length) {
13536             
13537             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13538
13539             cfg.cn = [
13540                 indicator,
13541                 {
13542                     tag: 'label',
13543                     'for' :  id,
13544                     cls : 'control-label',
13545                     html : this.fieldLabel
13546
13547                 },
13548                 {
13549                     cls : "", 
13550                     cn: [
13551                         combobox
13552                     ]
13553                 }
13554
13555             ];
13556             
13557             var labelCfg = cfg.cn[1];
13558             var contentCfg = cfg.cn[2];
13559             
13560             if(this.indicatorpos == 'right'){
13561                 cfg.cn = [
13562                     {
13563                         tag: 'label',
13564                         'for' :  id,
13565                         cls : 'control-label',
13566                         cn : [
13567                             {
13568                                 tag : 'span',
13569                                 html : this.fieldLabel
13570                             },
13571                             indicator
13572                         ]
13573                     },
13574                     {
13575                         cls : "", 
13576                         cn: [
13577                             combobox
13578                         ]
13579                     }
13580
13581                 ];
13582                 
13583                 labelCfg = cfg.cn[0];
13584                 contentCfg = cfg.cn[1];
13585             }
13586             
13587             if(this.labelWidth > 12){
13588                 labelCfg.style = "width: " + this.labelWidth + 'px';
13589             }
13590             
13591             if(this.labelWidth < 13 && this.labelmd == 0){
13592                 this.labelmd = this.labelWidth;
13593             }
13594             
13595             if(this.labellg > 0){
13596                 labelCfg.cls += ' col-lg-' + this.labellg;
13597                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13598             }
13599             
13600             if(this.labelmd > 0){
13601                 labelCfg.cls += ' col-md-' + this.labelmd;
13602                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13603             }
13604             
13605             if(this.labelsm > 0){
13606                 labelCfg.cls += ' col-sm-' + this.labelsm;
13607                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13608             }
13609             
13610             if(this.labelxs > 0){
13611                 labelCfg.cls += ' col-xs-' + this.labelxs;
13612                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13613             }
13614             
13615         } else if ( this.fieldLabel.length) {
13616 //                Roo.log(" label");
13617             cfg.cn = [
13618                 indicator,
13619                {
13620                    tag: 'label',
13621                    //cls : 'input-group-addon',
13622                    html : this.fieldLabel
13623
13624                },
13625
13626                combobox
13627
13628             ];
13629             
13630             if(this.indicatorpos == 'right'){
13631                 
13632                 cfg.cn = [
13633                     {
13634                        tag: 'label',
13635                        cn : [
13636                            {
13637                                tag : 'span',
13638                                html : this.fieldLabel
13639                            },
13640                            indicator
13641                        ]
13642
13643                     },
13644                     combobox
13645
13646                 ];
13647
13648             }
13649
13650         } else {
13651             
13652 //                Roo.log(" no label && no align");
13653                 cfg = combobox
13654                      
13655                 
13656         }
13657         
13658         var settings=this;
13659         ['xs','sm','md','lg'].map(function(size){
13660             if (settings[size]) {
13661                 cfg.cls += ' col-' + size + '-' + settings[size];
13662             }
13663         });
13664         
13665         return cfg;
13666         
13667     },
13668     
13669     
13670     
13671     // private
13672     onResize : function(w, h){
13673 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13674 //        if(typeof w == 'number'){
13675 //            var x = w - this.trigger.getWidth();
13676 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13677 //            this.trigger.setStyle('left', x+'px');
13678 //        }
13679     },
13680
13681     // private
13682     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13683
13684     // private
13685     getResizeEl : function(){
13686         return this.inputEl();
13687     },
13688
13689     // private
13690     getPositionEl : function(){
13691         return this.inputEl();
13692     },
13693
13694     // private
13695     alignErrorIcon : function(){
13696         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13697     },
13698
13699     // private
13700     initEvents : function(){
13701         
13702         this.createList();
13703         
13704         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13705         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13706         if(!this.multiple && this.showToggleBtn){
13707             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13708             if(this.hideTrigger){
13709                 this.trigger.setDisplayed(false);
13710             }
13711             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13712         }
13713         
13714         if(this.multiple){
13715             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13716         }
13717         
13718         if(this.removable && !this.editable && !this.tickable){
13719             var close = this.closeTriggerEl();
13720             
13721             if(close){
13722                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13723                 close.on('click', this.removeBtnClick, this, close);
13724             }
13725         }
13726         
13727         //this.trigger.addClassOnOver('x-form-trigger-over');
13728         //this.trigger.addClassOnClick('x-form-trigger-click');
13729         
13730         //if(!this.width){
13731         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13732         //}
13733     },
13734     
13735     closeTriggerEl : function()
13736     {
13737         var close = this.el.select('.roo-combo-removable-btn', true).first();
13738         return close ? close : false;
13739     },
13740     
13741     removeBtnClick : function(e, h, el)
13742     {
13743         e.preventDefault();
13744         
13745         if(this.fireEvent("remove", this) !== false){
13746             this.reset();
13747             this.fireEvent("afterremove", this)
13748         }
13749     },
13750     
13751     createList : function()
13752     {
13753         this.list = Roo.get(document.body).createChild({
13754             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13755             cls: 'typeahead typeahead-long dropdown-menu shadow',
13756             style: 'display:none'
13757         });
13758         
13759         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13760         
13761     },
13762
13763     // private
13764     initTrigger : function(){
13765        
13766     },
13767
13768     // private
13769     onDestroy : function(){
13770         if(this.trigger){
13771             this.trigger.removeAllListeners();
13772           //  this.trigger.remove();
13773         }
13774         //if(this.wrap){
13775         //    this.wrap.remove();
13776         //}
13777         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13778     },
13779
13780     // private
13781     onFocus : function(){
13782         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13783         /*
13784         if(!this.mimicing){
13785             this.wrap.addClass('x-trigger-wrap-focus');
13786             this.mimicing = true;
13787             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13788             if(this.monitorTab){
13789                 this.el.on("keydown", this.checkTab, this);
13790             }
13791         }
13792         */
13793     },
13794
13795     // private
13796     checkTab : function(e){
13797         if(e.getKey() == e.TAB){
13798             this.triggerBlur();
13799         }
13800     },
13801
13802     // private
13803     onBlur : function(){
13804         // do nothing
13805     },
13806
13807     // private
13808     mimicBlur : function(e, t){
13809         /*
13810         if(!this.wrap.contains(t) && this.validateBlur()){
13811             this.triggerBlur();
13812         }
13813         */
13814     },
13815
13816     // private
13817     triggerBlur : function(){
13818         this.mimicing = false;
13819         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13820         if(this.monitorTab){
13821             this.el.un("keydown", this.checkTab, this);
13822         }
13823         //this.wrap.removeClass('x-trigger-wrap-focus');
13824         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13825     },
13826
13827     // private
13828     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13829     validateBlur : function(e, t){
13830         return true;
13831     },
13832
13833     // private
13834     onDisable : function(){
13835         this.inputEl().dom.disabled = true;
13836         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13837         //if(this.wrap){
13838         //    this.wrap.addClass('x-item-disabled');
13839         //}
13840     },
13841
13842     // private
13843     onEnable : function(){
13844         this.inputEl().dom.disabled = false;
13845         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13846         //if(this.wrap){
13847         //    this.el.removeClass('x-item-disabled');
13848         //}
13849     },
13850
13851     // private
13852     onShow : function(){
13853         var ae = this.getActionEl();
13854         
13855         if(ae){
13856             ae.dom.style.display = '';
13857             ae.dom.style.visibility = 'visible';
13858         }
13859     },
13860
13861     // private
13862     
13863     onHide : function(){
13864         var ae = this.getActionEl();
13865         ae.dom.style.display = 'none';
13866     },
13867
13868     /**
13869      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13870      * by an implementing function.
13871      * @method
13872      * @param {EventObject} e
13873      */
13874     onTriggerClick : Roo.emptyFn
13875 });
13876  
13877 /*
13878 * Licence: LGPL
13879 */
13880
13881 /**
13882  * @class Roo.bootstrap.CardUploader
13883  * @extends Roo.bootstrap.Button
13884  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13885  * @cfg {Number} errorTimeout default 3000
13886  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13887  * @cfg {Array}  html The button text.
13888
13889  *
13890  * @constructor
13891  * Create a new CardUploader
13892  * @param {Object} config The config object
13893  */
13894
13895 Roo.bootstrap.CardUploader = function(config){
13896     
13897  
13898     
13899     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13900     
13901     
13902     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13903         return r.data.id
13904      });
13905     
13906      this.addEvents({
13907          // raw events
13908         /**
13909          * @event preview
13910          * When a image is clicked on - and needs to display a slideshow or similar..
13911          * @param {Roo.bootstrap.Card} this
13912          * @param {Object} The image information data 
13913          *
13914          */
13915         'preview' : true,
13916          /**
13917          * @event download
13918          * When a the download link is clicked
13919          * @param {Roo.bootstrap.Card} this
13920          * @param {Object} The image information data  contains 
13921          */
13922         'download' : true
13923         
13924     });
13925 };
13926  
13927 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13928     
13929      
13930     errorTimeout : 3000,
13931      
13932     images : false,
13933    
13934     fileCollection : false,
13935     allowBlank : true,
13936     
13937     getAutoCreate : function()
13938     {
13939         
13940         var cfg =  {
13941             cls :'form-group' ,
13942             cn : [
13943                
13944                 {
13945                     tag: 'label',
13946                    //cls : 'input-group-addon',
13947                     html : this.fieldLabel
13948
13949                 },
13950
13951                 {
13952                     tag: 'input',
13953                     type : 'hidden',
13954                     name : this.name,
13955                     value : this.value,
13956                     cls : 'd-none  form-control'
13957                 },
13958                 
13959                 {
13960                     tag: 'input',
13961                     multiple : 'multiple',
13962                     type : 'file',
13963                     cls : 'd-none  roo-card-upload-selector'
13964                 },
13965                 
13966                 {
13967                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13968                 },
13969                 {
13970                     cls : 'card-columns roo-card-uploader-container'
13971                 }
13972
13973             ]
13974         };
13975            
13976          
13977         return cfg;
13978     },
13979     
13980     getChildContainer : function() /// what children are added to.
13981     {
13982         return this.containerEl;
13983     },
13984    
13985     getButtonContainer : function() /// what children are added to.
13986     {
13987         return this.el.select(".roo-card-uploader-button-container").first();
13988     },
13989    
13990     initEvents : function()
13991     {
13992         
13993         Roo.bootstrap.Input.prototype.initEvents.call(this);
13994         
13995         var t = this;
13996         this.addxtype({
13997             xns: Roo.bootstrap,
13998
13999             xtype : 'Button',
14000             container_method : 'getButtonContainer' ,            
14001             html :  this.html, // fix changable?
14002             cls : 'w-100 ',
14003             listeners : {
14004                 'click' : function(btn, e) {
14005                     t.onClick(e);
14006                 }
14007             }
14008         });
14009         
14010         
14011         
14012         
14013         this.urlAPI = (window.createObjectURL && window) || 
14014                                 (window.URL && URL.revokeObjectURL && URL) || 
14015                                 (window.webkitURL && webkitURL);
14016                         
14017          
14018          
14019          
14020         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14021         
14022         this.selectorEl.on('change', this.onFileSelected, this);
14023         if (this.images) {
14024             var t = this;
14025             this.images.forEach(function(img) {
14026                 t.addCard(img)
14027             });
14028             this.images = false;
14029         }
14030         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14031          
14032        
14033     },
14034     
14035    
14036     onClick : function(e)
14037     {
14038         e.preventDefault();
14039          
14040         this.selectorEl.dom.click();
14041          
14042     },
14043     
14044     onFileSelected : function(e)
14045     {
14046         e.preventDefault();
14047         
14048         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14049             return;
14050         }
14051         
14052         Roo.each(this.selectorEl.dom.files, function(file){    
14053             this.addFile(file);
14054         }, this);
14055          
14056     },
14057     
14058       
14059     
14060       
14061     
14062     addFile : function(file)
14063     {
14064            
14065         if(typeof(file) === 'string'){
14066             throw "Add file by name?"; // should not happen
14067             return;
14068         }
14069         
14070         if(!file || !this.urlAPI){
14071             return;
14072         }
14073         
14074         // file;
14075         // file.type;
14076         
14077         var _this = this;
14078         
14079         
14080         var url = _this.urlAPI.createObjectURL( file);
14081            
14082         this.addCard({
14083             id : Roo.bootstrap.CardUploader.ID--,
14084             is_uploaded : false,
14085             src : url,
14086             srcfile : file,
14087             title : file.name,
14088             mimetype : file.type,
14089             preview : false,
14090             is_deleted : 0
14091         });
14092         
14093     },
14094     
14095     /**
14096      * addCard - add an Attachment to the uploader
14097      * @param data - the data about the image to upload
14098      *
14099      * {
14100           id : 123
14101           title : "Title of file",
14102           is_uploaded : false,
14103           src : "http://.....",
14104           srcfile : { the File upload object },
14105           mimetype : file.type,
14106           preview : false,
14107           is_deleted : 0
14108           .. any other data...
14109         }
14110      *
14111      * 
14112     */
14113     
14114     addCard : function (data)
14115     {
14116         // hidden input element?
14117         // if the file is not an image...
14118         //then we need to use something other that and header_image
14119         var t = this;
14120         //   remove.....
14121         var footer = [
14122             {
14123                 xns : Roo.bootstrap,
14124                 xtype : 'CardFooter',
14125                  items: [
14126                     {
14127                         xns : Roo.bootstrap,
14128                         xtype : 'Element',
14129                         cls : 'd-flex',
14130                         items : [
14131                             
14132                             {
14133                                 xns : Roo.bootstrap,
14134                                 xtype : 'Button',
14135                                 html : String.format("<small>{0}</small>", data.title),
14136                                 cls : 'col-10 text-left',
14137                                 size: 'sm',
14138                                 weight: 'link',
14139                                 fa : 'download',
14140                                 listeners : {
14141                                     click : function() {
14142                                      
14143                                         t.fireEvent( "download", t, data );
14144                                     }
14145                                 }
14146                             },
14147                           
14148                             {
14149                                 xns : Roo.bootstrap,
14150                                 xtype : 'Button',
14151                                 style: 'max-height: 28px; ',
14152                                 size : 'sm',
14153                                 weight: 'danger',
14154                                 cls : 'col-2',
14155                                 fa : 'times',
14156                                 listeners : {
14157                                     click : function() {
14158                                         t.removeCard(data.id)
14159                                     }
14160                                 }
14161                             }
14162                         ]
14163                     }
14164                     
14165                 ] 
14166             }
14167             
14168         ];
14169         
14170         var cn = this.addxtype(
14171             {
14172                  
14173                 xns : Roo.bootstrap,
14174                 xtype : 'Card',
14175                 closeable : true,
14176                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14177                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14178                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14179                 data : data,
14180                 html : false,
14181                  
14182                 items : footer,
14183                 initEvents : function() {
14184                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14185                     var card = this;
14186                     this.imgEl = this.el.select('.card-img-top').first();
14187                     if (this.imgEl) {
14188                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14189                         this.imgEl.set({ 'pointer' : 'cursor' });
14190                                   
14191                     }
14192                     this.getCardFooter().addClass('p-1');
14193                     
14194                   
14195                 }
14196                 
14197             }
14198         );
14199         // dont' really need ot update items.
14200         // this.items.push(cn);
14201         this.fileCollection.add(cn);
14202         
14203         if (!data.srcfile) {
14204             this.updateInput();
14205             return;
14206         }
14207             
14208         var _t = this;
14209         var reader = new FileReader();
14210         reader.addEventListener("load", function() {  
14211             data.srcdata =  reader.result;
14212             _t.updateInput();
14213         });
14214         reader.readAsDataURL(data.srcfile);
14215         
14216         
14217         
14218     },
14219     removeCard : function(id)
14220     {
14221         
14222         var card  = this.fileCollection.get(id);
14223         card.data.is_deleted = 1;
14224         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14225         //this.fileCollection.remove(card);
14226         //this.items = this.items.filter(function(e) { return e != card });
14227         // dont' really need ot update items.
14228         card.el.dom.parentNode.removeChild(card.el.dom);
14229         this.updateInput();
14230
14231         
14232     },
14233     reset: function()
14234     {
14235         this.fileCollection.each(function(card) {
14236             if (card.el.dom && card.el.dom.parentNode) {
14237                 card.el.dom.parentNode.removeChild(card.el.dom);
14238             }
14239         });
14240         this.fileCollection.clear();
14241         this.updateInput();
14242     },
14243     
14244     updateInput : function()
14245     {
14246          var data = [];
14247         this.fileCollection.each(function(e) {
14248             data.push(e.data);
14249             
14250         });
14251         this.inputEl().dom.value = JSON.stringify(data);
14252         
14253         
14254         
14255     }
14256     
14257     
14258 });
14259
14260
14261 Roo.bootstrap.CardUploader.ID = -1;/*
14262  * Based on:
14263  * Ext JS Library 1.1.1
14264  * Copyright(c) 2006-2007, Ext JS, LLC.
14265  *
14266  * Originally Released Under LGPL - original licence link has changed is not relivant.
14267  *
14268  * Fork - LGPL
14269  * <script type="text/javascript">
14270  */
14271
14272
14273 /**
14274  * @class Roo.data.SortTypes
14275  * @singleton
14276  * Defines the default sorting (casting?) comparison functions used when sorting data.
14277  */
14278 Roo.data.SortTypes = {
14279     /**
14280      * Default sort that does nothing
14281      * @param {Mixed} s The value being converted
14282      * @return {Mixed} The comparison value
14283      */
14284     none : function(s){
14285         return s;
14286     },
14287     
14288     /**
14289      * The regular expression used to strip tags
14290      * @type {RegExp}
14291      * @property
14292      */
14293     stripTagsRE : /<\/?[^>]+>/gi,
14294     
14295     /**
14296      * Strips all HTML tags to sort on text only
14297      * @param {Mixed} s The value being converted
14298      * @return {String} The comparison value
14299      */
14300     asText : function(s){
14301         return String(s).replace(this.stripTagsRE, "");
14302     },
14303     
14304     /**
14305      * Strips all HTML tags to sort on text only - Case insensitive
14306      * @param {Mixed} s The value being converted
14307      * @return {String} The comparison value
14308      */
14309     asUCText : function(s){
14310         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14311     },
14312     
14313     /**
14314      * Case insensitive string
14315      * @param {Mixed} s The value being converted
14316      * @return {String} The comparison value
14317      */
14318     asUCString : function(s) {
14319         return String(s).toUpperCase();
14320     },
14321     
14322     /**
14323      * Date sorting
14324      * @param {Mixed} s The value being converted
14325      * @return {Number} The comparison value
14326      */
14327     asDate : function(s) {
14328         if(!s){
14329             return 0;
14330         }
14331         if(s instanceof Date){
14332             return s.getTime();
14333         }
14334         return Date.parse(String(s));
14335     },
14336     
14337     /**
14338      * Float sorting
14339      * @param {Mixed} s The value being converted
14340      * @return {Float} The comparison value
14341      */
14342     asFloat : function(s) {
14343         var val = parseFloat(String(s).replace(/,/g, ""));
14344         if(isNaN(val)) {
14345             val = 0;
14346         }
14347         return val;
14348     },
14349     
14350     /**
14351      * Integer sorting
14352      * @param {Mixed} s The value being converted
14353      * @return {Number} The comparison value
14354      */
14355     asInt : function(s) {
14356         var val = parseInt(String(s).replace(/,/g, ""));
14357         if(isNaN(val)) {
14358             val = 0;
14359         }
14360         return val;
14361     }
14362 };/*
14363  * Based on:
14364  * Ext JS Library 1.1.1
14365  * Copyright(c) 2006-2007, Ext JS, LLC.
14366  *
14367  * Originally Released Under LGPL - original licence link has changed is not relivant.
14368  *
14369  * Fork - LGPL
14370  * <script type="text/javascript">
14371  */
14372
14373 /**
14374 * @class Roo.data.Record
14375  * Instances of this class encapsulate both record <em>definition</em> information, and record
14376  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14377  * to access Records cached in an {@link Roo.data.Store} object.<br>
14378  * <p>
14379  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14380  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14381  * objects.<br>
14382  * <p>
14383  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14384  * @constructor
14385  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14386  * {@link #create}. The parameters are the same.
14387  * @param {Array} data An associative Array of data values keyed by the field name.
14388  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14389  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14390  * not specified an integer id is generated.
14391  */
14392 Roo.data.Record = function(data, id){
14393     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14394     this.data = data;
14395 };
14396
14397 /**
14398  * Generate a constructor for a specific record layout.
14399  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14400  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14401  * Each field definition object may contain the following properties: <ul>
14402  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14403  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14404  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14405  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14406  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14407  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14408  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14409  * this may be omitted.</p></li>
14410  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14411  * <ul><li>auto (Default, implies no conversion)</li>
14412  * <li>string</li>
14413  * <li>int</li>
14414  * <li>float</li>
14415  * <li>boolean</li>
14416  * <li>date</li></ul></p></li>
14417  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14418  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14419  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14420  * by the Reader into an object that will be stored in the Record. It is passed the
14421  * following parameters:<ul>
14422  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14423  * </ul></p></li>
14424  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14425  * </ul>
14426  * <br>usage:<br><pre><code>
14427 var TopicRecord = Roo.data.Record.create(
14428     {name: 'title', mapping: 'topic_title'},
14429     {name: 'author', mapping: 'username'},
14430     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14431     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14432     {name: 'lastPoster', mapping: 'user2'},
14433     {name: 'excerpt', mapping: 'post_text'}
14434 );
14435
14436 var myNewRecord = new TopicRecord({
14437     title: 'Do my job please',
14438     author: 'noobie',
14439     totalPosts: 1,
14440     lastPost: new Date(),
14441     lastPoster: 'Animal',
14442     excerpt: 'No way dude!'
14443 });
14444 myStore.add(myNewRecord);
14445 </code></pre>
14446  * @method create
14447  * @static
14448  */
14449 Roo.data.Record.create = function(o){
14450     var f = function(){
14451         f.superclass.constructor.apply(this, arguments);
14452     };
14453     Roo.extend(f, Roo.data.Record);
14454     var p = f.prototype;
14455     p.fields = new Roo.util.MixedCollection(false, function(field){
14456         return field.name;
14457     });
14458     for(var i = 0, len = o.length; i < len; i++){
14459         p.fields.add(new Roo.data.Field(o[i]));
14460     }
14461     f.getField = function(name){
14462         return p.fields.get(name);  
14463     };
14464     return f;
14465 };
14466
14467 Roo.data.Record.AUTO_ID = 1000;
14468 Roo.data.Record.EDIT = 'edit';
14469 Roo.data.Record.REJECT = 'reject';
14470 Roo.data.Record.COMMIT = 'commit';
14471
14472 Roo.data.Record.prototype = {
14473     /**
14474      * Readonly flag - true if this record has been modified.
14475      * @type Boolean
14476      */
14477     dirty : false,
14478     editing : false,
14479     error: null,
14480     modified: null,
14481
14482     // private
14483     join : function(store){
14484         this.store = store;
14485     },
14486
14487     /**
14488      * Set the named field to the specified value.
14489      * @param {String} name The name of the field to set.
14490      * @param {Object} value The value to set the field to.
14491      */
14492     set : function(name, value){
14493         if(this.data[name] == value){
14494             return;
14495         }
14496         this.dirty = true;
14497         if(!this.modified){
14498             this.modified = {};
14499         }
14500         if(typeof this.modified[name] == 'undefined'){
14501             this.modified[name] = this.data[name];
14502         }
14503         this.data[name] = value;
14504         if(!this.editing && this.store){
14505             this.store.afterEdit(this);
14506         }       
14507     },
14508
14509     /**
14510      * Get the value of the named field.
14511      * @param {String} name The name of the field to get the value of.
14512      * @return {Object} The value of the field.
14513      */
14514     get : function(name){
14515         return this.data[name]; 
14516     },
14517
14518     // private
14519     beginEdit : function(){
14520         this.editing = true;
14521         this.modified = {}; 
14522     },
14523
14524     // private
14525     cancelEdit : function(){
14526         this.editing = false;
14527         delete this.modified;
14528     },
14529
14530     // private
14531     endEdit : function(){
14532         this.editing = false;
14533         if(this.dirty && this.store){
14534             this.store.afterEdit(this);
14535         }
14536     },
14537
14538     /**
14539      * Usually called by the {@link Roo.data.Store} which owns the Record.
14540      * Rejects all changes made to the Record since either creation, or the last commit operation.
14541      * Modified fields are reverted to their original values.
14542      * <p>
14543      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14544      * of reject operations.
14545      */
14546     reject : function(){
14547         var m = this.modified;
14548         for(var n in m){
14549             if(typeof m[n] != "function"){
14550                 this.data[n] = m[n];
14551             }
14552         }
14553         this.dirty = false;
14554         delete this.modified;
14555         this.editing = false;
14556         if(this.store){
14557             this.store.afterReject(this);
14558         }
14559     },
14560
14561     /**
14562      * Usually called by the {@link Roo.data.Store} which owns the Record.
14563      * Commits all changes made to the Record since either creation, or the last commit operation.
14564      * <p>
14565      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14566      * of commit operations.
14567      */
14568     commit : function(){
14569         this.dirty = false;
14570         delete this.modified;
14571         this.editing = false;
14572         if(this.store){
14573             this.store.afterCommit(this);
14574         }
14575     },
14576
14577     // private
14578     hasError : function(){
14579         return this.error != null;
14580     },
14581
14582     // private
14583     clearError : function(){
14584         this.error = null;
14585     },
14586
14587     /**
14588      * Creates a copy of this record.
14589      * @param {String} id (optional) A new record id if you don't want to use this record's id
14590      * @return {Record}
14591      */
14592     copy : function(newId) {
14593         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14594     }
14595 };/*
14596  * Based on:
14597  * Ext JS Library 1.1.1
14598  * Copyright(c) 2006-2007, Ext JS, LLC.
14599  *
14600  * Originally Released Under LGPL - original licence link has changed is not relivant.
14601  *
14602  * Fork - LGPL
14603  * <script type="text/javascript">
14604  */
14605
14606
14607
14608 /**
14609  * @class Roo.data.Store
14610  * @extends Roo.util.Observable
14611  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14612  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14613  * <p>
14614  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
14615  * has no knowledge of the format of the data returned by the Proxy.<br>
14616  * <p>
14617  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14618  * instances from the data object. These records are cached and made available through accessor functions.
14619  * @constructor
14620  * Creates a new Store.
14621  * @param {Object} config A config object containing the objects needed for the Store to access data,
14622  * and read the data into Records.
14623  */
14624 Roo.data.Store = function(config){
14625     this.data = new Roo.util.MixedCollection(false);
14626     this.data.getKey = function(o){
14627         return o.id;
14628     };
14629     this.baseParams = {};
14630     // private
14631     this.paramNames = {
14632         "start" : "start",
14633         "limit" : "limit",
14634         "sort" : "sort",
14635         "dir" : "dir",
14636         "multisort" : "_multisort"
14637     };
14638
14639     if(config && config.data){
14640         this.inlineData = config.data;
14641         delete config.data;
14642     }
14643
14644     Roo.apply(this, config);
14645     
14646     if(this.reader){ // reader passed
14647         this.reader = Roo.factory(this.reader, Roo.data);
14648         this.reader.xmodule = this.xmodule || false;
14649         if(!this.recordType){
14650             this.recordType = this.reader.recordType;
14651         }
14652         if(this.reader.onMetaChange){
14653             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14654         }
14655     }
14656
14657     if(this.recordType){
14658         this.fields = this.recordType.prototype.fields;
14659     }
14660     this.modified = [];
14661
14662     this.addEvents({
14663         /**
14664          * @event datachanged
14665          * Fires when the data cache has changed, and a widget which is using this Store
14666          * as a Record cache should refresh its view.
14667          * @param {Store} this
14668          */
14669         datachanged : true,
14670         /**
14671          * @event metachange
14672          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14673          * @param {Store} this
14674          * @param {Object} meta The JSON metadata
14675          */
14676         metachange : true,
14677         /**
14678          * @event add
14679          * Fires when Records have been added to the Store
14680          * @param {Store} this
14681          * @param {Roo.data.Record[]} records The array of Records added
14682          * @param {Number} index The index at which the record(s) were added
14683          */
14684         add : true,
14685         /**
14686          * @event remove
14687          * Fires when a Record has been removed from the Store
14688          * @param {Store} this
14689          * @param {Roo.data.Record} record The Record that was removed
14690          * @param {Number} index The index at which the record was removed
14691          */
14692         remove : true,
14693         /**
14694          * @event update
14695          * Fires when a Record has been updated
14696          * @param {Store} this
14697          * @param {Roo.data.Record} record The Record that was updated
14698          * @param {String} operation The update operation being performed.  Value may be one of:
14699          * <pre><code>
14700  Roo.data.Record.EDIT
14701  Roo.data.Record.REJECT
14702  Roo.data.Record.COMMIT
14703          * </code></pre>
14704          */
14705         update : true,
14706         /**
14707          * @event clear
14708          * Fires when the data cache has been cleared.
14709          * @param {Store} this
14710          */
14711         clear : true,
14712         /**
14713          * @event beforeload
14714          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14715          * the load action will be canceled.
14716          * @param {Store} this
14717          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14718          */
14719         beforeload : true,
14720         /**
14721          * @event beforeloadadd
14722          * Fires after a new set of Records has been loaded.
14723          * @param {Store} this
14724          * @param {Roo.data.Record[]} records The Records that were loaded
14725          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14726          */
14727         beforeloadadd : true,
14728         /**
14729          * @event load
14730          * Fires after a new set of Records has been loaded, before they are added to the store.
14731          * @param {Store} this
14732          * @param {Roo.data.Record[]} records The Records that were loaded
14733          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14734          * @params {Object} return from reader
14735          */
14736         load : true,
14737         /**
14738          * @event loadexception
14739          * Fires if an exception occurs in the Proxy during loading.
14740          * Called with the signature of the Proxy's "loadexception" event.
14741          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14742          * 
14743          * @param {Proxy} 
14744          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14745          * @param {Object} load options 
14746          * @param {Object} jsonData from your request (normally this contains the Exception)
14747          */
14748         loadexception : true
14749     });
14750     
14751     if(this.proxy){
14752         this.proxy = Roo.factory(this.proxy, Roo.data);
14753         this.proxy.xmodule = this.xmodule || false;
14754         this.relayEvents(this.proxy,  ["loadexception"]);
14755     }
14756     this.sortToggle = {};
14757     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14758
14759     Roo.data.Store.superclass.constructor.call(this);
14760
14761     if(this.inlineData){
14762         this.loadData(this.inlineData);
14763         delete this.inlineData;
14764     }
14765 };
14766
14767 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14768      /**
14769     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14770     * without a remote query - used by combo/forms at present.
14771     */
14772     
14773     /**
14774     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14775     */
14776     /**
14777     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14778     */
14779     /**
14780     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14781     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14782     */
14783     /**
14784     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14785     * on any HTTP request
14786     */
14787     /**
14788     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14789     */
14790     /**
14791     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14792     */
14793     multiSort: false,
14794     /**
14795     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14796     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14797     */
14798     remoteSort : false,
14799
14800     /**
14801     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14802      * loaded or when a record is removed. (defaults to false).
14803     */
14804     pruneModifiedRecords : false,
14805
14806     // private
14807     lastOptions : null,
14808
14809     /**
14810      * Add Records to the Store and fires the add event.
14811      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14812      */
14813     add : function(records){
14814         records = [].concat(records);
14815         for(var i = 0, len = records.length; i < len; i++){
14816             records[i].join(this);
14817         }
14818         var index = this.data.length;
14819         this.data.addAll(records);
14820         this.fireEvent("add", this, records, index);
14821     },
14822
14823     /**
14824      * Remove a Record from the Store and fires the remove event.
14825      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14826      */
14827     remove : function(record){
14828         var index = this.data.indexOf(record);
14829         this.data.removeAt(index);
14830  
14831         if(this.pruneModifiedRecords){
14832             this.modified.remove(record);
14833         }
14834         this.fireEvent("remove", this, record, index);
14835     },
14836
14837     /**
14838      * Remove all Records from the Store and fires the clear event.
14839      */
14840     removeAll : function(){
14841         this.data.clear();
14842         if(this.pruneModifiedRecords){
14843             this.modified = [];
14844         }
14845         this.fireEvent("clear", this);
14846     },
14847
14848     /**
14849      * Inserts Records to the Store at the given index and fires the add event.
14850      * @param {Number} index The start index at which to insert the passed Records.
14851      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14852      */
14853     insert : function(index, records){
14854         records = [].concat(records);
14855         for(var i = 0, len = records.length; i < len; i++){
14856             this.data.insert(index, records[i]);
14857             records[i].join(this);
14858         }
14859         this.fireEvent("add", this, records, index);
14860     },
14861
14862     /**
14863      * Get the index within the cache of the passed Record.
14864      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14865      * @return {Number} The index of the passed Record. Returns -1 if not found.
14866      */
14867     indexOf : function(record){
14868         return this.data.indexOf(record);
14869     },
14870
14871     /**
14872      * Get the index within the cache of the Record with the passed id.
14873      * @param {String} id The id of the Record to find.
14874      * @return {Number} The index of the Record. Returns -1 if not found.
14875      */
14876     indexOfId : function(id){
14877         return this.data.indexOfKey(id);
14878     },
14879
14880     /**
14881      * Get the Record with the specified id.
14882      * @param {String} id The id of the Record to find.
14883      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14884      */
14885     getById : function(id){
14886         return this.data.key(id);
14887     },
14888
14889     /**
14890      * Get the Record at the specified index.
14891      * @param {Number} index The index of the Record to find.
14892      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14893      */
14894     getAt : function(index){
14895         return this.data.itemAt(index);
14896     },
14897
14898     /**
14899      * Returns a range of Records between specified indices.
14900      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14901      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14902      * @return {Roo.data.Record[]} An array of Records
14903      */
14904     getRange : function(start, end){
14905         return this.data.getRange(start, end);
14906     },
14907
14908     // private
14909     storeOptions : function(o){
14910         o = Roo.apply({}, o);
14911         delete o.callback;
14912         delete o.scope;
14913         this.lastOptions = o;
14914     },
14915
14916     /**
14917      * Loads the Record cache from the configured Proxy using the configured Reader.
14918      * <p>
14919      * If using remote paging, then the first load call must specify the <em>start</em>
14920      * and <em>limit</em> properties in the options.params property to establish the initial
14921      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14922      * <p>
14923      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14924      * and this call will return before the new data has been loaded. Perform any post-processing
14925      * in a callback function, or in a "load" event handler.</strong>
14926      * <p>
14927      * @param {Object} options An object containing properties which control loading options:<ul>
14928      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14929      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14930      * passed the following arguments:<ul>
14931      * <li>r : Roo.data.Record[]</li>
14932      * <li>options: Options object from the load call</li>
14933      * <li>success: Boolean success indicator</li></ul></li>
14934      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14935      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14936      * </ul>
14937      */
14938     load : function(options){
14939         options = options || {};
14940         if(this.fireEvent("beforeload", this, options) !== false){
14941             this.storeOptions(options);
14942             var p = Roo.apply(options.params || {}, this.baseParams);
14943             // if meta was not loaded from remote source.. try requesting it.
14944             if (!this.reader.metaFromRemote) {
14945                 p._requestMeta = 1;
14946             }
14947             if(this.sortInfo && this.remoteSort){
14948                 var pn = this.paramNames;
14949                 p[pn["sort"]] = this.sortInfo.field;
14950                 p[pn["dir"]] = this.sortInfo.direction;
14951             }
14952             if (this.multiSort) {
14953                 var pn = this.paramNames;
14954                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14955             }
14956             
14957             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14958         }
14959     },
14960
14961     /**
14962      * Reloads the Record cache from the configured Proxy using the configured Reader and
14963      * the options from the last load operation performed.
14964      * @param {Object} options (optional) An object containing properties which may override the options
14965      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14966      * the most recently used options are reused).
14967      */
14968     reload : function(options){
14969         this.load(Roo.applyIf(options||{}, this.lastOptions));
14970     },
14971
14972     // private
14973     // Called as a callback by the Reader during a load operation.
14974     loadRecords : function(o, options, success){
14975         if(!o || success === false){
14976             if(success !== false){
14977                 this.fireEvent("load", this, [], options, o);
14978             }
14979             if(options.callback){
14980                 options.callback.call(options.scope || this, [], options, false);
14981             }
14982             return;
14983         }
14984         // if data returned failure - throw an exception.
14985         if (o.success === false) {
14986             // show a message if no listener is registered.
14987             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14988                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14989             }
14990             // loadmask wil be hooked into this..
14991             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14992             return;
14993         }
14994         var r = o.records, t = o.totalRecords || r.length;
14995         
14996         this.fireEvent("beforeloadadd", this, r, options, o);
14997         
14998         if(!options || options.add !== true){
14999             if(this.pruneModifiedRecords){
15000                 this.modified = [];
15001             }
15002             for(var i = 0, len = r.length; i < len; i++){
15003                 r[i].join(this);
15004             }
15005             if(this.snapshot){
15006                 this.data = this.snapshot;
15007                 delete this.snapshot;
15008             }
15009             this.data.clear();
15010             this.data.addAll(r);
15011             this.totalLength = t;
15012             this.applySort();
15013             this.fireEvent("datachanged", this);
15014         }else{
15015             this.totalLength = Math.max(t, this.data.length+r.length);
15016             this.add(r);
15017         }
15018         
15019         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15020                 
15021             var e = new Roo.data.Record({});
15022
15023             e.set(this.parent.displayField, this.parent.emptyTitle);
15024             e.set(this.parent.valueField, '');
15025
15026             this.insert(0, e);
15027         }
15028             
15029         this.fireEvent("load", this, r, options, o);
15030         if(options.callback){
15031             options.callback.call(options.scope || this, r, options, true);
15032         }
15033     },
15034
15035
15036     /**
15037      * Loads data from a passed data block. A Reader which understands the format of the data
15038      * must have been configured in the constructor.
15039      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15040      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15041      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15042      */
15043     loadData : function(o, append){
15044         var r = this.reader.readRecords(o);
15045         this.loadRecords(r, {add: append}, true);
15046     },
15047     
15048      /**
15049      * using 'cn' the nested child reader read the child array into it's child stores.
15050      * @param {Object} rec The record with a 'children array
15051      */
15052     loadDataFromChildren : function(rec)
15053     {
15054         this.loadData(this.reader.toLoadData(rec));
15055     },
15056     
15057
15058     /**
15059      * Gets the number of cached records.
15060      * <p>
15061      * <em>If using paging, this may not be the total size of the dataset. If the data object
15062      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15063      * the data set size</em>
15064      */
15065     getCount : function(){
15066         return this.data.length || 0;
15067     },
15068
15069     /**
15070      * Gets the total number of records in the dataset as returned by the server.
15071      * <p>
15072      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15073      * the dataset size</em>
15074      */
15075     getTotalCount : function(){
15076         return this.totalLength || 0;
15077     },
15078
15079     /**
15080      * Returns the sort state of the Store as an object with two properties:
15081      * <pre><code>
15082  field {String} The name of the field by which the Records are sorted
15083  direction {String} The sort order, "ASC" or "DESC"
15084      * </code></pre>
15085      */
15086     getSortState : function(){
15087         return this.sortInfo;
15088     },
15089
15090     // private
15091     applySort : function(){
15092         if(this.sortInfo && !this.remoteSort){
15093             var s = this.sortInfo, f = s.field;
15094             var st = this.fields.get(f).sortType;
15095             var fn = function(r1, r2){
15096                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15097                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15098             };
15099             this.data.sort(s.direction, fn);
15100             if(this.snapshot && this.snapshot != this.data){
15101                 this.snapshot.sort(s.direction, fn);
15102             }
15103         }
15104     },
15105
15106     /**
15107      * Sets the default sort column and order to be used by the next load operation.
15108      * @param {String} fieldName The name of the field to sort by.
15109      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15110      */
15111     setDefaultSort : function(field, dir){
15112         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15113     },
15114
15115     /**
15116      * Sort the Records.
15117      * If remote sorting is used, the sort is performed on the server, and the cache is
15118      * reloaded. If local sorting is used, the cache is sorted internally.
15119      * @param {String} fieldName The name of the field to sort by.
15120      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15121      */
15122     sort : function(fieldName, dir){
15123         var f = this.fields.get(fieldName);
15124         if(!dir){
15125             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15126             
15127             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15128                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15129             }else{
15130                 dir = f.sortDir;
15131             }
15132         }
15133         this.sortToggle[f.name] = dir;
15134         this.sortInfo = {field: f.name, direction: dir};
15135         if(!this.remoteSort){
15136             this.applySort();
15137             this.fireEvent("datachanged", this);
15138         }else{
15139             this.load(this.lastOptions);
15140         }
15141     },
15142
15143     /**
15144      * Calls the specified function for each of the Records in the cache.
15145      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15146      * Returning <em>false</em> aborts and exits the iteration.
15147      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15148      */
15149     each : function(fn, scope){
15150         this.data.each(fn, scope);
15151     },
15152
15153     /**
15154      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15155      * (e.g., during paging).
15156      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15157      */
15158     getModifiedRecords : function(){
15159         return this.modified;
15160     },
15161
15162     // private
15163     createFilterFn : function(property, value, anyMatch){
15164         if(!value.exec){ // not a regex
15165             value = String(value);
15166             if(value.length == 0){
15167                 return false;
15168             }
15169             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15170         }
15171         return function(r){
15172             return value.test(r.data[property]);
15173         };
15174     },
15175
15176     /**
15177      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15178      * @param {String} property A field on your records
15179      * @param {Number} start The record index to start at (defaults to 0)
15180      * @param {Number} end The last record index to include (defaults to length - 1)
15181      * @return {Number} The sum
15182      */
15183     sum : function(property, start, end){
15184         var rs = this.data.items, v = 0;
15185         start = start || 0;
15186         end = (end || end === 0) ? end : rs.length-1;
15187
15188         for(var i = start; i <= end; i++){
15189             v += (rs[i].data[property] || 0);
15190         }
15191         return v;
15192     },
15193
15194     /**
15195      * Filter the records by a specified property.
15196      * @param {String} field A field on your records
15197      * @param {String/RegExp} value Either a string that the field
15198      * should start with or a RegExp to test against the field
15199      * @param {Boolean} anyMatch True to match any part not just the beginning
15200      */
15201     filter : function(property, value, anyMatch){
15202         var fn = this.createFilterFn(property, value, anyMatch);
15203         return fn ? this.filterBy(fn) : this.clearFilter();
15204     },
15205
15206     /**
15207      * Filter by a function. The specified function will be called with each
15208      * record in this data source. If the function returns true the record is included,
15209      * otherwise it is filtered.
15210      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15211      * @param {Object} scope (optional) The scope of the function (defaults to this)
15212      */
15213     filterBy : function(fn, scope){
15214         this.snapshot = this.snapshot || this.data;
15215         this.data = this.queryBy(fn, scope||this);
15216         this.fireEvent("datachanged", this);
15217     },
15218
15219     /**
15220      * Query the records by a specified property.
15221      * @param {String} field A field on your records
15222      * @param {String/RegExp} value Either a string that the field
15223      * should start with or a RegExp to test against the field
15224      * @param {Boolean} anyMatch True to match any part not just the beginning
15225      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15226      */
15227     query : function(property, value, anyMatch){
15228         var fn = this.createFilterFn(property, value, anyMatch);
15229         return fn ? this.queryBy(fn) : this.data.clone();
15230     },
15231
15232     /**
15233      * Query by a function. The specified function will be called with each
15234      * record in this data source. If the function returns true the record is included
15235      * in the results.
15236      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15237      * @param {Object} scope (optional) The scope of the function (defaults to this)
15238       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15239      **/
15240     queryBy : function(fn, scope){
15241         var data = this.snapshot || this.data;
15242         return data.filterBy(fn, scope||this);
15243     },
15244
15245     /**
15246      * Collects unique values for a particular dataIndex from this store.
15247      * @param {String} dataIndex The property to collect
15248      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15249      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15250      * @return {Array} An array of the unique values
15251      **/
15252     collect : function(dataIndex, allowNull, bypassFilter){
15253         var d = (bypassFilter === true && this.snapshot) ?
15254                 this.snapshot.items : this.data.items;
15255         var v, sv, r = [], l = {};
15256         for(var i = 0, len = d.length; i < len; i++){
15257             v = d[i].data[dataIndex];
15258             sv = String(v);
15259             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15260                 l[sv] = true;
15261                 r[r.length] = v;
15262             }
15263         }
15264         return r;
15265     },
15266
15267     /**
15268      * Revert to a view of the Record cache with no filtering applied.
15269      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15270      */
15271     clearFilter : function(suppressEvent){
15272         if(this.snapshot && this.snapshot != this.data){
15273             this.data = this.snapshot;
15274             delete this.snapshot;
15275             if(suppressEvent !== true){
15276                 this.fireEvent("datachanged", this);
15277             }
15278         }
15279     },
15280
15281     // private
15282     afterEdit : function(record){
15283         if(this.modified.indexOf(record) == -1){
15284             this.modified.push(record);
15285         }
15286         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15287     },
15288     
15289     // private
15290     afterReject : function(record){
15291         this.modified.remove(record);
15292         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15293     },
15294
15295     // private
15296     afterCommit : function(record){
15297         this.modified.remove(record);
15298         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15299     },
15300
15301     /**
15302      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15303      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15304      */
15305     commitChanges : function(){
15306         var m = this.modified.slice(0);
15307         this.modified = [];
15308         for(var i = 0, len = m.length; i < len; i++){
15309             m[i].commit();
15310         }
15311     },
15312
15313     /**
15314      * Cancel outstanding changes on all changed records.
15315      */
15316     rejectChanges : function(){
15317         var m = this.modified.slice(0);
15318         this.modified = [];
15319         for(var i = 0, len = m.length; i < len; i++){
15320             m[i].reject();
15321         }
15322     },
15323
15324     onMetaChange : function(meta, rtype, o){
15325         this.recordType = rtype;
15326         this.fields = rtype.prototype.fields;
15327         delete this.snapshot;
15328         this.sortInfo = meta.sortInfo || this.sortInfo;
15329         this.modified = [];
15330         this.fireEvent('metachange', this, this.reader.meta);
15331     },
15332     
15333     moveIndex : function(data, type)
15334     {
15335         var index = this.indexOf(data);
15336         
15337         var newIndex = index + type;
15338         
15339         this.remove(data);
15340         
15341         this.insert(newIndex, data);
15342         
15343     }
15344 });/*
15345  * Based on:
15346  * Ext JS Library 1.1.1
15347  * Copyright(c) 2006-2007, Ext JS, LLC.
15348  *
15349  * Originally Released Under LGPL - original licence link has changed is not relivant.
15350  *
15351  * Fork - LGPL
15352  * <script type="text/javascript">
15353  */
15354
15355 /**
15356  * @class Roo.data.SimpleStore
15357  * @extends Roo.data.Store
15358  * Small helper class to make creating Stores from Array data easier.
15359  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15360  * @cfg {Array} fields An array of field definition objects, or field name strings.
15361  * @cfg {Object} an existing reader (eg. copied from another store)
15362  * @cfg {Array} data The multi-dimensional array of data
15363  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15364  * @cfg {Roo.data.Reader} reader  [not-required] 
15365  * @constructor
15366  * @param {Object} config
15367  */
15368 Roo.data.SimpleStore = function(config)
15369 {
15370     Roo.data.SimpleStore.superclass.constructor.call(this, {
15371         isLocal : true,
15372         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15373                 id: config.id
15374             },
15375             Roo.data.Record.create(config.fields)
15376         ),
15377         proxy : new Roo.data.MemoryProxy(config.data)
15378     });
15379     this.load();
15380 };
15381 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15382  * Based on:
15383  * Ext JS Library 1.1.1
15384  * Copyright(c) 2006-2007, Ext JS, LLC.
15385  *
15386  * Originally Released Under LGPL - original licence link has changed is not relivant.
15387  *
15388  * Fork - LGPL
15389  * <script type="text/javascript">
15390  */
15391
15392 /**
15393 /**
15394  * @extends Roo.data.Store
15395  * @class Roo.data.JsonStore
15396  * Small helper class to make creating Stores for JSON data easier. <br/>
15397 <pre><code>
15398 var store = new Roo.data.JsonStore({
15399     url: 'get-images.php',
15400     root: 'images',
15401     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15402 });
15403 </code></pre>
15404  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15405  * JsonReader and HttpProxy (unless inline data is provided).</b>
15406  * @cfg {Array} fields An array of field definition objects, or field name strings.
15407  * @constructor
15408  * @param {Object} config
15409  */
15410 Roo.data.JsonStore = function(c){
15411     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15412         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15413         reader: new Roo.data.JsonReader(c, c.fields)
15414     }));
15415 };
15416 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15417  * Based on:
15418  * Ext JS Library 1.1.1
15419  * Copyright(c) 2006-2007, Ext JS, LLC.
15420  *
15421  * Originally Released Under LGPL - original licence link has changed is not relivant.
15422  *
15423  * Fork - LGPL
15424  * <script type="text/javascript">
15425  */
15426
15427  
15428 Roo.data.Field = function(config){
15429     if(typeof config == "string"){
15430         config = {name: config};
15431     }
15432     Roo.apply(this, config);
15433     
15434     if(!this.type){
15435         this.type = "auto";
15436     }
15437     
15438     var st = Roo.data.SortTypes;
15439     // named sortTypes are supported, here we look them up
15440     if(typeof this.sortType == "string"){
15441         this.sortType = st[this.sortType];
15442     }
15443     
15444     // set default sortType for strings and dates
15445     if(!this.sortType){
15446         switch(this.type){
15447             case "string":
15448                 this.sortType = st.asUCString;
15449                 break;
15450             case "date":
15451                 this.sortType = st.asDate;
15452                 break;
15453             default:
15454                 this.sortType = st.none;
15455         }
15456     }
15457
15458     // define once
15459     var stripRe = /[\$,%]/g;
15460
15461     // prebuilt conversion function for this field, instead of
15462     // switching every time we're reading a value
15463     if(!this.convert){
15464         var cv, dateFormat = this.dateFormat;
15465         switch(this.type){
15466             case "":
15467             case "auto":
15468             case undefined:
15469                 cv = function(v){ return v; };
15470                 break;
15471             case "string":
15472                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15473                 break;
15474             case "int":
15475                 cv = function(v){
15476                     return v !== undefined && v !== null && v !== '' ?
15477                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15478                     };
15479                 break;
15480             case "float":
15481                 cv = function(v){
15482                     return v !== undefined && v !== null && v !== '' ?
15483                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15484                     };
15485                 break;
15486             case "bool":
15487             case "boolean":
15488                 cv = function(v){ return v === true || v === "true" || v == 1; };
15489                 break;
15490             case "date":
15491                 cv = function(v){
15492                     if(!v){
15493                         return '';
15494                     }
15495                     if(v instanceof Date){
15496                         return v;
15497                     }
15498                     if(dateFormat){
15499                         if(dateFormat == "timestamp"){
15500                             return new Date(v*1000);
15501                         }
15502                         return Date.parseDate(v, dateFormat);
15503                     }
15504                     var parsed = Date.parse(v);
15505                     return parsed ? new Date(parsed) : null;
15506                 };
15507              break;
15508             
15509         }
15510         this.convert = cv;
15511     }
15512 };
15513
15514 Roo.data.Field.prototype = {
15515     dateFormat: null,
15516     defaultValue: "",
15517     mapping: null,
15518     sortType : null,
15519     sortDir : "ASC"
15520 };/*
15521  * Based on:
15522  * Ext JS Library 1.1.1
15523  * Copyright(c) 2006-2007, Ext JS, LLC.
15524  *
15525  * Originally Released Under LGPL - original licence link has changed is not relivant.
15526  *
15527  * Fork - LGPL
15528  * <script type="text/javascript">
15529  */
15530  
15531 // Base class for reading structured data from a data source.  This class is intended to be
15532 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15533
15534 /**
15535  * @class Roo.data.DataReader
15536  * @abstract
15537  * Base class for reading structured data from a data source.  This class is intended to be
15538  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15539  */
15540
15541 Roo.data.DataReader = function(meta, recordType){
15542     
15543     this.meta = meta;
15544     
15545     this.recordType = recordType instanceof Array ? 
15546         Roo.data.Record.create(recordType) : recordType;
15547 };
15548
15549 Roo.data.DataReader.prototype = {
15550     
15551     
15552     readerType : 'Data',
15553      /**
15554      * Create an empty record
15555      * @param {Object} data (optional) - overlay some values
15556      * @return {Roo.data.Record} record created.
15557      */
15558     newRow :  function(d) {
15559         var da =  {};
15560         this.recordType.prototype.fields.each(function(c) {
15561             switch( c.type) {
15562                 case 'int' : da[c.name] = 0; break;
15563                 case 'date' : da[c.name] = new Date(); break;
15564                 case 'float' : da[c.name] = 0.0; break;
15565                 case 'boolean' : da[c.name] = false; break;
15566                 default : da[c.name] = ""; break;
15567             }
15568             
15569         });
15570         return new this.recordType(Roo.apply(da, d));
15571     }
15572     
15573     
15574 };/*
15575  * Based on:
15576  * Ext JS Library 1.1.1
15577  * Copyright(c) 2006-2007, Ext JS, LLC.
15578  *
15579  * Originally Released Under LGPL - original licence link has changed is not relivant.
15580  *
15581  * Fork - LGPL
15582  * <script type="text/javascript">
15583  */
15584
15585 /**
15586  * @class Roo.data.DataProxy
15587  * @extends Roo.data.Observable
15588  * @abstract
15589  * This class is an abstract base class for implementations which provide retrieval of
15590  * unformatted data objects.<br>
15591  * <p>
15592  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15593  * (of the appropriate type which knows how to parse the data object) to provide a block of
15594  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15595  * <p>
15596  * Custom implementations must implement the load method as described in
15597  * {@link Roo.data.HttpProxy#load}.
15598  */
15599 Roo.data.DataProxy = function(){
15600     this.addEvents({
15601         /**
15602          * @event beforeload
15603          * Fires before a network request is made to retrieve a data object.
15604          * @param {Object} This DataProxy object.
15605          * @param {Object} params The params parameter to the load function.
15606          */
15607         beforeload : true,
15608         /**
15609          * @event load
15610          * Fires before the load method's callback is called.
15611          * @param {Object} This DataProxy object.
15612          * @param {Object} o The data object.
15613          * @param {Object} arg The callback argument object passed to the load function.
15614          */
15615         load : true,
15616         /**
15617          * @event loadexception
15618          * Fires if an Exception occurs during data retrieval.
15619          * @param {Object} This DataProxy object.
15620          * @param {Object} o The data object.
15621          * @param {Object} arg The callback argument object passed to the load function.
15622          * @param {Object} e The Exception.
15623          */
15624         loadexception : true
15625     });
15626     Roo.data.DataProxy.superclass.constructor.call(this);
15627 };
15628
15629 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15630
15631     /**
15632      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15633      */
15634 /*
15635  * Based on:
15636  * Ext JS Library 1.1.1
15637  * Copyright(c) 2006-2007, Ext JS, LLC.
15638  *
15639  * Originally Released Under LGPL - original licence link has changed is not relivant.
15640  *
15641  * Fork - LGPL
15642  * <script type="text/javascript">
15643  */
15644 /**
15645  * @class Roo.data.MemoryProxy
15646  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15647  * to the Reader when its load method is called.
15648  * @constructor
15649  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15650  */
15651 Roo.data.MemoryProxy = function(data){
15652     if (data.data) {
15653         data = data.data;
15654     }
15655     Roo.data.MemoryProxy.superclass.constructor.call(this);
15656     this.data = data;
15657 };
15658
15659 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15660     
15661     /**
15662      * Load data from the requested source (in this case an in-memory
15663      * data object passed to the constructor), read the data object into
15664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15665      * process that block using the passed callback.
15666      * @param {Object} params This parameter is not used by the MemoryProxy class.
15667      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15668      * object into a block of Roo.data.Records.
15669      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15670      * The function must be passed <ul>
15671      * <li>The Record block object</li>
15672      * <li>The "arg" argument from the load function</li>
15673      * <li>A boolean success indicator</li>
15674      * </ul>
15675      * @param {Object} scope The scope in which to call the callback
15676      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15677      */
15678     load : function(params, reader, callback, scope, arg){
15679         params = params || {};
15680         var result;
15681         try {
15682             result = reader.readRecords(params.data ? params.data :this.data);
15683         }catch(e){
15684             this.fireEvent("loadexception", this, arg, null, e);
15685             callback.call(scope, null, arg, false);
15686             return;
15687         }
15688         callback.call(scope, result, arg, true);
15689     },
15690     
15691     // private
15692     update : function(params, records){
15693         
15694     }
15695 });/*
15696  * Based on:
15697  * Ext JS Library 1.1.1
15698  * Copyright(c) 2006-2007, Ext JS, LLC.
15699  *
15700  * Originally Released Under LGPL - original licence link has changed is not relivant.
15701  *
15702  * Fork - LGPL
15703  * <script type="text/javascript">
15704  */
15705 /**
15706  * @class Roo.data.HttpProxy
15707  * @extends Roo.data.DataProxy
15708  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15709  * configured to reference a certain URL.<br><br>
15710  * <p>
15711  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15712  * from which the running page was served.<br><br>
15713  * <p>
15714  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15715  * <p>
15716  * Be aware that to enable the browser to parse an XML document, the server must set
15717  * the Content-Type header in the HTTP response to "text/xml".
15718  * @constructor
15719  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15720  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15721  * will be used to make the request.
15722  */
15723 Roo.data.HttpProxy = function(conn){
15724     Roo.data.HttpProxy.superclass.constructor.call(this);
15725     // is conn a conn config or a real conn?
15726     this.conn = conn;
15727     this.useAjax = !conn || !conn.events;
15728   
15729 };
15730
15731 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15732     // thse are take from connection...
15733     
15734     /**
15735      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15736      */
15737     /**
15738      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15739      * extra parameters to each request made by this object. (defaults to undefined)
15740      */
15741     /**
15742      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15743      *  to each request made by this object. (defaults to undefined)
15744      */
15745     /**
15746      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
15747      */
15748     /**
15749      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15750      */
15751      /**
15752      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15753      * @type Boolean
15754      */
15755   
15756
15757     /**
15758      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15759      * @type Boolean
15760      */
15761     /**
15762      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15763      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15764      * a finer-grained basis than the DataProxy events.
15765      */
15766     getConnection : function(){
15767         return this.useAjax ? Roo.Ajax : this.conn;
15768     },
15769
15770     /**
15771      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15772      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15773      * process that block using the passed callback.
15774      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15775      * for the request to the remote server.
15776      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15777      * object into a block of Roo.data.Records.
15778      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15779      * The function must be passed <ul>
15780      * <li>The Record block object</li>
15781      * <li>The "arg" argument from the load function</li>
15782      * <li>A boolean success indicator</li>
15783      * </ul>
15784      * @param {Object} scope The scope in which to call the callback
15785      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15786      */
15787     load : function(params, reader, callback, scope, arg){
15788         if(this.fireEvent("beforeload", this, params) !== false){
15789             var  o = {
15790                 params : params || {},
15791                 request: {
15792                     callback : callback,
15793                     scope : scope,
15794                     arg : arg
15795                 },
15796                 reader: reader,
15797                 callback : this.loadResponse,
15798                 scope: this
15799             };
15800             if(this.useAjax){
15801                 Roo.applyIf(o, this.conn);
15802                 if(this.activeRequest){
15803                     Roo.Ajax.abort(this.activeRequest);
15804                 }
15805                 this.activeRequest = Roo.Ajax.request(o);
15806             }else{
15807                 this.conn.request(o);
15808             }
15809         }else{
15810             callback.call(scope||this, null, arg, false);
15811         }
15812     },
15813
15814     // private
15815     loadResponse : function(o, success, response){
15816         delete this.activeRequest;
15817         if(!success){
15818             this.fireEvent("loadexception", this, o, response);
15819             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15820             return;
15821         }
15822         var result;
15823         try {
15824             result = o.reader.read(response);
15825         }catch(e){
15826             this.fireEvent("loadexception", this, o, response, e);
15827             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15828             return;
15829         }
15830         
15831         this.fireEvent("load", this, o, o.request.arg);
15832         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15833     },
15834
15835     // private
15836     update : function(dataSet){
15837
15838     },
15839
15840     // private
15841     updateResponse : function(dataSet){
15842
15843     }
15844 });/*
15845  * Based on:
15846  * Ext JS Library 1.1.1
15847  * Copyright(c) 2006-2007, Ext JS, LLC.
15848  *
15849  * Originally Released Under LGPL - original licence link has changed is not relivant.
15850  *
15851  * Fork - LGPL
15852  * <script type="text/javascript">
15853  */
15854
15855 /**
15856  * @class Roo.data.ScriptTagProxy
15857  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15858  * other than the originating domain of the running page.<br><br>
15859  * <p>
15860  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
15861  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15862  * <p>
15863  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15864  * source code that is used as the source inside a &lt;script> tag.<br><br>
15865  * <p>
15866  * In order for the browser to process the returned data, the server must wrap the data object
15867  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15868  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15869  * depending on whether the callback name was passed:
15870  * <p>
15871  * <pre><code>
15872 boolean scriptTag = false;
15873 String cb = request.getParameter("callback");
15874 if (cb != null) {
15875     scriptTag = true;
15876     response.setContentType("text/javascript");
15877 } else {
15878     response.setContentType("application/x-json");
15879 }
15880 Writer out = response.getWriter();
15881 if (scriptTag) {
15882     out.write(cb + "(");
15883 }
15884 out.print(dataBlock.toJsonString());
15885 if (scriptTag) {
15886     out.write(");");
15887 }
15888 </pre></code>
15889  *
15890  * @constructor
15891  * @param {Object} config A configuration object.
15892  */
15893 Roo.data.ScriptTagProxy = function(config){
15894     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15895     Roo.apply(this, config);
15896     this.head = document.getElementsByTagName("head")[0];
15897 };
15898
15899 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15900
15901 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15902     /**
15903      * @cfg {String} url The URL from which to request the data object.
15904      */
15905     /**
15906      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15907      */
15908     timeout : 30000,
15909     /**
15910      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15911      * the server the name of the callback function set up by the load call to process the returned data object.
15912      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15913      * javascript output which calls this named function passing the data object as its only parameter.
15914      */
15915     callbackParam : "callback",
15916     /**
15917      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15918      * name to the request.
15919      */
15920     nocache : true,
15921
15922     /**
15923      * Load data from the configured URL, read the data object into
15924      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15925      * process that block using the passed callback.
15926      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15927      * for the request to the remote server.
15928      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15929      * object into a block of Roo.data.Records.
15930      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15931      * The function must be passed <ul>
15932      * <li>The Record block object</li>
15933      * <li>The "arg" argument from the load function</li>
15934      * <li>A boolean success indicator</li>
15935      * </ul>
15936      * @param {Object} scope The scope in which to call the callback
15937      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15938      */
15939     load : function(params, reader, callback, scope, arg){
15940         if(this.fireEvent("beforeload", this, params) !== false){
15941
15942             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15943
15944             var url = this.url;
15945             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15946             if(this.nocache){
15947                 url += "&_dc=" + (new Date().getTime());
15948             }
15949             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15950             var trans = {
15951                 id : transId,
15952                 cb : "stcCallback"+transId,
15953                 scriptId : "stcScript"+transId,
15954                 params : params,
15955                 arg : arg,
15956                 url : url,
15957                 callback : callback,
15958                 scope : scope,
15959                 reader : reader
15960             };
15961             var conn = this;
15962
15963             window[trans.cb] = function(o){
15964                 conn.handleResponse(o, trans);
15965             };
15966
15967             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15968
15969             if(this.autoAbort !== false){
15970                 this.abort();
15971             }
15972
15973             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15974
15975             var script = document.createElement("script");
15976             script.setAttribute("src", url);
15977             script.setAttribute("type", "text/javascript");
15978             script.setAttribute("id", trans.scriptId);
15979             this.head.appendChild(script);
15980
15981             this.trans = trans;
15982         }else{
15983             callback.call(scope||this, null, arg, false);
15984         }
15985     },
15986
15987     // private
15988     isLoading : function(){
15989         return this.trans ? true : false;
15990     },
15991
15992     /**
15993      * Abort the current server request.
15994      */
15995     abort : function(){
15996         if(this.isLoading()){
15997             this.destroyTrans(this.trans);
15998         }
15999     },
16000
16001     // private
16002     destroyTrans : function(trans, isLoaded){
16003         this.head.removeChild(document.getElementById(trans.scriptId));
16004         clearTimeout(trans.timeoutId);
16005         if(isLoaded){
16006             window[trans.cb] = undefined;
16007             try{
16008                 delete window[trans.cb];
16009             }catch(e){}
16010         }else{
16011             // if hasn't been loaded, wait for load to remove it to prevent script error
16012             window[trans.cb] = function(){
16013                 window[trans.cb] = undefined;
16014                 try{
16015                     delete window[trans.cb];
16016                 }catch(e){}
16017             };
16018         }
16019     },
16020
16021     // private
16022     handleResponse : function(o, trans){
16023         this.trans = false;
16024         this.destroyTrans(trans, true);
16025         var result;
16026         try {
16027             result = trans.reader.readRecords(o);
16028         }catch(e){
16029             this.fireEvent("loadexception", this, o, trans.arg, e);
16030             trans.callback.call(trans.scope||window, null, trans.arg, false);
16031             return;
16032         }
16033         this.fireEvent("load", this, o, trans.arg);
16034         trans.callback.call(trans.scope||window, result, trans.arg, true);
16035     },
16036
16037     // private
16038     handleFailure : function(trans){
16039         this.trans = false;
16040         this.destroyTrans(trans, false);
16041         this.fireEvent("loadexception", this, null, trans.arg);
16042         trans.callback.call(trans.scope||window, null, trans.arg, false);
16043     }
16044 });/*
16045  * Based on:
16046  * Ext JS Library 1.1.1
16047  * Copyright(c) 2006-2007, Ext JS, LLC.
16048  *
16049  * Originally Released Under LGPL - original licence link has changed is not relivant.
16050  *
16051  * Fork - LGPL
16052  * <script type="text/javascript">
16053  */
16054
16055 /**
16056  * @class Roo.data.JsonReader
16057  * @extends Roo.data.DataReader
16058  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16059  * based on mappings in a provided Roo.data.Record constructor.
16060  * 
16061  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16062  * in the reply previously. 
16063  * 
16064  * <p>
16065  * Example code:
16066  * <pre><code>
16067 var RecordDef = Roo.data.Record.create([
16068     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16069     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16070 ]);
16071 var myReader = new Roo.data.JsonReader({
16072     totalProperty: "results",    // The property which contains the total dataset size (optional)
16073     root: "rows",                // The property which contains an Array of row objects
16074     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16075 }, RecordDef);
16076 </code></pre>
16077  * <p>
16078  * This would consume a JSON file like this:
16079  * <pre><code>
16080 { 'results': 2, 'rows': [
16081     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16082     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16083 }
16084 </code></pre>
16085  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16086  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16087  * paged from the remote server.
16088  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16089  * @cfg {String} root name of the property which contains the Array of row objects.
16090  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16091  * @cfg {Array} fields Array of field definition objects
16092  * @constructor
16093  * Create a new JsonReader
16094  * @param {Object} meta Metadata configuration options
16095  * @param {Object} recordType Either an Array of field definition objects,
16096  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16097  */
16098 Roo.data.JsonReader = function(meta, recordType){
16099     
16100     meta = meta || {};
16101     // set some defaults:
16102     Roo.applyIf(meta, {
16103         totalProperty: 'total',
16104         successProperty : 'success',
16105         root : 'data',
16106         id : 'id'
16107     });
16108     
16109     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16110 };
16111 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16112     
16113     readerType : 'Json',
16114     
16115     /**
16116      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16117      * Used by Store query builder to append _requestMeta to params.
16118      * 
16119      */
16120     metaFromRemote : false,
16121     /**
16122      * This method is only used by a DataProxy which has retrieved data from a remote server.
16123      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16124      * @return {Object} data A data block which is used by an Roo.data.Store object as
16125      * a cache of Roo.data.Records.
16126      */
16127     read : function(response){
16128         var json = response.responseText;
16129        
16130         var o = /* eval:var:o */ eval("("+json+")");
16131         if(!o) {
16132             throw {message: "JsonReader.read: Json object not found"};
16133         }
16134         
16135         if(o.metaData){
16136             
16137             delete this.ef;
16138             this.metaFromRemote = true;
16139             this.meta = o.metaData;
16140             this.recordType = Roo.data.Record.create(o.metaData.fields);
16141             this.onMetaChange(this.meta, this.recordType, o);
16142         }
16143         return this.readRecords(o);
16144     },
16145
16146     // private function a store will implement
16147     onMetaChange : function(meta, recordType, o){
16148
16149     },
16150
16151     /**
16152          * @ignore
16153          */
16154     simpleAccess: function(obj, subsc) {
16155         return obj[subsc];
16156     },
16157
16158         /**
16159          * @ignore
16160          */
16161     getJsonAccessor: function(){
16162         var re = /[\[\.]/;
16163         return function(expr) {
16164             try {
16165                 return(re.test(expr))
16166                     ? new Function("obj", "return obj." + expr)
16167                     : function(obj){
16168                         return obj[expr];
16169                     };
16170             } catch(e){}
16171             return Roo.emptyFn;
16172         };
16173     }(),
16174
16175     /**
16176      * Create a data block containing Roo.data.Records from an XML document.
16177      * @param {Object} o An object which contains an Array of row objects in the property specified
16178      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16179      * which contains the total size of the dataset.
16180      * @return {Object} data A data block which is used by an Roo.data.Store object as
16181      * a cache of Roo.data.Records.
16182      */
16183     readRecords : function(o){
16184         /**
16185          * After any data loads, the raw JSON data is available for further custom processing.
16186          * @type Object
16187          */
16188         this.o = o;
16189         var s = this.meta, Record = this.recordType,
16190             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16191
16192 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16193         if (!this.ef) {
16194             if(s.totalProperty) {
16195                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16196                 }
16197                 if(s.successProperty) {
16198                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16199                 }
16200                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16201                 if (s.id) {
16202                         var g = this.getJsonAccessor(s.id);
16203                         this.getId = function(rec) {
16204                                 var r = g(rec);  
16205                                 return (r === undefined || r === "") ? null : r;
16206                         };
16207                 } else {
16208                         this.getId = function(){return null;};
16209                 }
16210             this.ef = [];
16211             for(var jj = 0; jj < fl; jj++){
16212                 f = fi[jj];
16213                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16214                 this.ef[jj] = this.getJsonAccessor(map);
16215             }
16216         }
16217
16218         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16219         if(s.totalProperty){
16220             var vt = parseInt(this.getTotal(o), 10);
16221             if(!isNaN(vt)){
16222                 totalRecords = vt;
16223             }
16224         }
16225         if(s.successProperty){
16226             var vs = this.getSuccess(o);
16227             if(vs === false || vs === 'false'){
16228                 success = false;
16229             }
16230         }
16231         var records = [];
16232         for(var i = 0; i < c; i++){
16233                 var n = root[i];
16234             var values = {};
16235             var id = this.getId(n);
16236             for(var j = 0; j < fl; j++){
16237                 f = fi[j];
16238             var v = this.ef[j](n);
16239             if (!f.convert) {
16240                 Roo.log('missing convert for ' + f.name);
16241                 Roo.log(f);
16242                 continue;
16243             }
16244             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16245             }
16246             var record = new Record(values, id);
16247             record.json = n;
16248             records[i] = record;
16249         }
16250         return {
16251             raw : o,
16252             success : success,
16253             records : records,
16254             totalRecords : totalRecords
16255         };
16256     },
16257     // used when loading children.. @see loadDataFromChildren
16258     toLoadData: function(rec)
16259     {
16260         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16261         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16262         return { data : data, total : data.length };
16263         
16264     }
16265 });/*
16266  * Based on:
16267  * Ext JS Library 1.1.1
16268  * Copyright(c) 2006-2007, Ext JS, LLC.
16269  *
16270  * Originally Released Under LGPL - original licence link has changed is not relivant.
16271  *
16272  * Fork - LGPL
16273  * <script type="text/javascript">
16274  */
16275
16276 /**
16277  * @class Roo.data.ArrayReader
16278  * @extends Roo.data.DataReader
16279  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16280  * Each element of that Array represents a row of data fields. The
16281  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16282  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16283  * <p>
16284  * Example code:.
16285  * <pre><code>
16286 var RecordDef = Roo.data.Record.create([
16287     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16288     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16289 ]);
16290 var myReader = new Roo.data.ArrayReader({
16291     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16292 }, RecordDef);
16293 </code></pre>
16294  * <p>
16295  * This would consume an Array like this:
16296  * <pre><code>
16297 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16298   </code></pre>
16299  
16300  * @constructor
16301  * Create a new JsonReader
16302  * @param {Object} meta Metadata configuration options.
16303  * @param {Object|Array} recordType Either an Array of field definition objects
16304  * 
16305  * @cfg {Array} fields Array of field definition objects
16306  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16307  * as specified to {@link Roo.data.Record#create},
16308  * or an {@link Roo.data.Record} object
16309  *
16310  * 
16311  * created using {@link Roo.data.Record#create}.
16312  */
16313 Roo.data.ArrayReader = function(meta, recordType)
16314 {    
16315     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16316 };
16317
16318 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16319     
16320       /**
16321      * Create a data block containing Roo.data.Records from an XML document.
16322      * @param {Object} o An Array of row objects which represents the dataset.
16323      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16324      * a cache of Roo.data.Records.
16325      */
16326     readRecords : function(o)
16327     {
16328         var sid = this.meta ? this.meta.id : null;
16329         var recordType = this.recordType, fields = recordType.prototype.fields;
16330         var records = [];
16331         var root = o;
16332         for(var i = 0; i < root.length; i++){
16333             var n = root[i];
16334             var values = {};
16335             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16336             for(var j = 0, jlen = fields.length; j < jlen; j++){
16337                 var f = fields.items[j];
16338                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16339                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16340                 v = f.convert(v);
16341                 values[f.name] = v;
16342             }
16343             var record = new recordType(values, id);
16344             record.json = n;
16345             records[records.length] = record;
16346         }
16347         return {
16348             records : records,
16349             totalRecords : records.length
16350         };
16351     },
16352     // used when loading children.. @see loadDataFromChildren
16353     toLoadData: function(rec)
16354     {
16355         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16356         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16357         
16358     }
16359     
16360     
16361 });/*
16362  * - LGPL
16363  * * 
16364  */
16365
16366 /**
16367  * @class Roo.bootstrap.ComboBox
16368  * @extends Roo.bootstrap.TriggerField
16369  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16370  * @cfg {Boolean} append (true|false) default false
16371  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16372  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16373  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16374  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16375  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16376  * @cfg {Boolean} animate default true
16377  * @cfg {Boolean} emptyResultText only for touch device
16378  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16379  * @cfg {String} emptyTitle default ''
16380  * @cfg {Number} width fixed with? experimental
16381  * @constructor
16382  * Create a new ComboBox.
16383  * @param {Object} config Configuration options
16384  */
16385 Roo.bootstrap.ComboBox = function(config){
16386     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16387     this.addEvents({
16388         /**
16389          * @event expand
16390          * Fires when the dropdown list is expanded
16391         * @param {Roo.bootstrap.ComboBox} combo This combo box
16392         */
16393         'expand' : true,
16394         /**
16395          * @event collapse
16396          * Fires when the dropdown list is collapsed
16397         * @param {Roo.bootstrap.ComboBox} combo This combo box
16398         */
16399         'collapse' : true,
16400         /**
16401          * @event beforeselect
16402          * Fires before a list item is selected. Return false to cancel the selection.
16403         * @param {Roo.bootstrap.ComboBox} combo This combo box
16404         * @param {Roo.data.Record} record The data record returned from the underlying store
16405         * @param {Number} index The index of the selected item in the dropdown list
16406         */
16407         'beforeselect' : true,
16408         /**
16409          * @event select
16410          * Fires when a list item is selected
16411         * @param {Roo.bootstrap.ComboBox} combo This combo box
16412         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16413         * @param {Number} index The index of the selected item in the dropdown list
16414         */
16415         'select' : true,
16416         /**
16417          * @event beforequery
16418          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16419          * The event object passed has these properties:
16420         * @param {Roo.bootstrap.ComboBox} combo This combo box
16421         * @param {String} query The query
16422         * @param {Boolean} forceAll true to force "all" query
16423         * @param {Boolean} cancel true to cancel the query
16424         * @param {Object} e The query event object
16425         */
16426         'beforequery': true,
16427          /**
16428          * @event add
16429          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16430         * @param {Roo.bootstrap.ComboBox} combo This combo box
16431         */
16432         'add' : true,
16433         /**
16434          * @event edit
16435          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16436         * @param {Roo.bootstrap.ComboBox} combo This combo box
16437         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16438         */
16439         'edit' : true,
16440         /**
16441          * @event remove
16442          * Fires when the remove value from the combobox array
16443         * @param {Roo.bootstrap.ComboBox} combo This combo box
16444         */
16445         'remove' : true,
16446         /**
16447          * @event afterremove
16448          * Fires when the remove value from the combobox array
16449         * @param {Roo.bootstrap.ComboBox} combo This combo box
16450         */
16451         'afterremove' : true,
16452         /**
16453          * @event specialfilter
16454          * Fires when specialfilter
16455             * @param {Roo.bootstrap.ComboBox} combo This combo box
16456             */
16457         'specialfilter' : true,
16458         /**
16459          * @event tick
16460          * Fires when tick the element
16461             * @param {Roo.bootstrap.ComboBox} combo This combo box
16462             */
16463         'tick' : true,
16464         /**
16465          * @event touchviewdisplay
16466          * Fires when touch view require special display (default is using displayField)
16467             * @param {Roo.bootstrap.ComboBox} combo This combo box
16468             * @param {Object} cfg set html .
16469             */
16470         'touchviewdisplay' : true
16471         
16472     });
16473     
16474     this.item = [];
16475     this.tickItems = [];
16476     
16477     this.selectedIndex = -1;
16478     if(this.mode == 'local'){
16479         if(config.queryDelay === undefined){
16480             this.queryDelay = 10;
16481         }
16482         if(config.minChars === undefined){
16483             this.minChars = 0;
16484         }
16485     }
16486 };
16487
16488 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16489      
16490     /**
16491      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16492      * rendering into an Roo.Editor, defaults to false)
16493      */
16494     /**
16495      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16496      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16497      */
16498     /**
16499      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16500      */
16501     /**
16502      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16503      * the dropdown list (defaults to undefined, with no header element)
16504      */
16505
16506      /**
16507      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16508      */
16509      
16510      /**
16511      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16512      */
16513     listWidth: undefined,
16514     /**
16515      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16516      * mode = 'remote' or 'text' if mode = 'local')
16517      */
16518     displayField: undefined,
16519     
16520     /**
16521      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16522      * mode = 'remote' or 'value' if mode = 'local'). 
16523      * Note: use of a valueField requires the user make a selection
16524      * in order for a value to be mapped.
16525      */
16526     valueField: undefined,
16527     /**
16528      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16529      */
16530     modalTitle : '',
16531     
16532     /**
16533      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16534      * field's data value (defaults to the underlying DOM element's name)
16535      */
16536     hiddenName: undefined,
16537     /**
16538      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16539      */
16540     listClass: '',
16541     /**
16542      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16543      */
16544     selectedClass: 'active',
16545     
16546     /**
16547      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16548      */
16549     shadow:'sides',
16550     /**
16551      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16552      * anchor positions (defaults to 'tl-bl')
16553      */
16554     listAlign: 'tl-bl?',
16555     /**
16556      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16557      */
16558     maxHeight: 300,
16559     /**
16560      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16561      * query specified by the allQuery config option (defaults to 'query')
16562      */
16563     triggerAction: 'query',
16564     /**
16565      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16566      * (defaults to 4, does not apply if editable = false)
16567      */
16568     minChars : 4,
16569     /**
16570      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16571      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16572      */
16573     typeAhead: false,
16574     /**
16575      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16576      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16577      */
16578     queryDelay: 500,
16579     /**
16580      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16581      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16582      */
16583     pageSize: 0,
16584     /**
16585      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16586      * when editable = true (defaults to false)
16587      */
16588     selectOnFocus:false,
16589     /**
16590      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16591      */
16592     queryParam: 'query',
16593     /**
16594      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16595      * when mode = 'remote' (defaults to 'Loading...')
16596      */
16597     loadingText: 'Loading...',
16598     /**
16599      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16600      */
16601     resizable: false,
16602     /**
16603      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16604      */
16605     handleHeight : 8,
16606     /**
16607      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16608      * traditional select (defaults to true)
16609      */
16610     editable: true,
16611     /**
16612      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16613      */
16614     allQuery: '',
16615     /**
16616      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16617      */
16618     mode: 'remote',
16619     /**
16620      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16621      * listWidth has a higher value)
16622      */
16623     minListWidth : 70,
16624     /**
16625      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16626      * allow the user to set arbitrary text into the field (defaults to false)
16627      */
16628     forceSelection:false,
16629     /**
16630      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16631      * if typeAhead = true (defaults to 250)
16632      */
16633     typeAheadDelay : 250,
16634     /**
16635      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16636      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16637      */
16638     valueNotFoundText : undefined,
16639     /**
16640      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16641      */
16642     blockFocus : false,
16643     
16644     /**
16645      * @cfg {Boolean} disableClear Disable showing of clear button.
16646      */
16647     disableClear : false,
16648     /**
16649      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16650      */
16651     alwaysQuery : false,
16652     
16653     /**
16654      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16655      */
16656     multiple : false,
16657     
16658     /**
16659      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16660      */
16661     invalidClass : "has-warning",
16662     
16663     /**
16664      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16665      */
16666     validClass : "has-success",
16667     
16668     /**
16669      * @cfg {Boolean} specialFilter (true|false) special filter default false
16670      */
16671     specialFilter : false,
16672     
16673     /**
16674      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16675      */
16676     mobileTouchView : true,
16677     
16678     /**
16679      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16680      */
16681     useNativeIOS : false,
16682     
16683     /**
16684      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16685      */
16686     mobile_restrict_height : false,
16687     
16688     ios_options : false,
16689     
16690     //private
16691     addicon : false,
16692     editicon: false,
16693     
16694     page: 0,
16695     hasQuery: false,
16696     append: false,
16697     loadNext: false,
16698     autoFocus : true,
16699     tickable : false,
16700     btnPosition : 'right',
16701     triggerList : true,
16702     showToggleBtn : true,
16703     animate : true,
16704     emptyResultText: 'Empty',
16705     triggerText : 'Select',
16706     emptyTitle : '',
16707     width : false,
16708     
16709     // element that contains real text value.. (when hidden is used..)
16710     
16711     getAutoCreate : function()
16712     {   
16713         var cfg = false;
16714         //render
16715         /*
16716          * Render classic select for iso
16717          */
16718         
16719         if(Roo.isIOS && this.useNativeIOS){
16720             cfg = this.getAutoCreateNativeIOS();
16721             return cfg;
16722         }
16723         
16724         /*
16725          * Touch Devices
16726          */
16727         
16728         if(Roo.isTouch && this.mobileTouchView){
16729             cfg = this.getAutoCreateTouchView();
16730             return cfg;;
16731         }
16732         
16733         /*
16734          *  Normal ComboBox
16735          */
16736         if(!this.tickable){
16737             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16738             return cfg;
16739         }
16740         
16741         /*
16742          *  ComboBox with tickable selections
16743          */
16744              
16745         var align = this.labelAlign || this.parentLabelAlign();
16746         
16747         cfg = {
16748             cls : 'form-group roo-combobox-tickable' //input-group
16749         };
16750         
16751         var btn_text_select = '';
16752         var btn_text_done = '';
16753         var btn_text_cancel = '';
16754         
16755         if (this.btn_text_show) {
16756             btn_text_select = 'Select';
16757             btn_text_done = 'Done';
16758             btn_text_cancel = 'Cancel'; 
16759         }
16760         
16761         var buttons = {
16762             tag : 'div',
16763             cls : 'tickable-buttons',
16764             cn : [
16765                 {
16766                     tag : 'button',
16767                     type : 'button',
16768                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16769                     //html : this.triggerText
16770                     html: btn_text_select
16771                 },
16772                 {
16773                     tag : 'button',
16774                     type : 'button',
16775                     name : 'ok',
16776                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16777                     //html : 'Done'
16778                     html: btn_text_done
16779                 },
16780                 {
16781                     tag : 'button',
16782                     type : 'button',
16783                     name : 'cancel',
16784                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16785                     //html : 'Cancel'
16786                     html: btn_text_cancel
16787                 }
16788             ]
16789         };
16790         
16791         if(this.editable){
16792             buttons.cn.unshift({
16793                 tag: 'input',
16794                 cls: 'roo-select2-search-field-input'
16795             });
16796         }
16797         
16798         var _this = this;
16799         
16800         Roo.each(buttons.cn, function(c){
16801             if (_this.size) {
16802                 c.cls += ' btn-' + _this.size;
16803             }
16804
16805             if (_this.disabled) {
16806                 c.disabled = true;
16807             }
16808         });
16809         
16810         var box = {
16811             tag: 'div',
16812             style : 'display: contents',
16813             cn: [
16814                 {
16815                     tag: 'input',
16816                     type : 'hidden',
16817                     cls: 'form-hidden-field'
16818                 },
16819                 {
16820                     tag: 'ul',
16821                     cls: 'roo-select2-choices',
16822                     cn:[
16823                         {
16824                             tag: 'li',
16825                             cls: 'roo-select2-search-field',
16826                             cn: [
16827                                 buttons
16828                             ]
16829                         }
16830                     ]
16831                 }
16832             ]
16833         };
16834         
16835         var combobox = {
16836             cls: 'roo-select2-container input-group roo-select2-container-multi',
16837             cn: [
16838                 
16839                 box
16840 //                {
16841 //                    tag: 'ul',
16842 //                    cls: 'typeahead typeahead-long dropdown-menu',
16843 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16844 //                }
16845             ]
16846         };
16847         
16848         if(this.hasFeedback && !this.allowBlank){
16849             
16850             var feedback = {
16851                 tag: 'span',
16852                 cls: 'glyphicon form-control-feedback'
16853             };
16854
16855             combobox.cn.push(feedback);
16856         }
16857         
16858         
16859         
16860         var indicator = {
16861             tag : 'i',
16862             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16863             tooltip : 'This field is required'
16864         };
16865         if (Roo.bootstrap.version == 4) {
16866             indicator = {
16867                 tag : 'i',
16868                 style : 'display:none'
16869             };
16870         }
16871         if (align ==='left' && this.fieldLabel.length) {
16872             
16873             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16874             
16875             cfg.cn = [
16876                 indicator,
16877                 {
16878                     tag: 'label',
16879                     'for' :  id,
16880                     cls : 'control-label col-form-label',
16881                     html : this.fieldLabel
16882
16883                 },
16884                 {
16885                     cls : "", 
16886                     cn: [
16887                         combobox
16888                     ]
16889                 }
16890
16891             ];
16892             
16893             var labelCfg = cfg.cn[1];
16894             var contentCfg = cfg.cn[2];
16895             
16896
16897             if(this.indicatorpos == 'right'){
16898                 
16899                 cfg.cn = [
16900                     {
16901                         tag: 'label',
16902                         'for' :  id,
16903                         cls : 'control-label col-form-label',
16904                         cn : [
16905                             {
16906                                 tag : 'span',
16907                                 html : this.fieldLabel
16908                             },
16909                             indicator
16910                         ]
16911                     },
16912                     {
16913                         cls : "",
16914                         cn: [
16915                             combobox
16916                         ]
16917                     }
16918
16919                 ];
16920                 
16921                 
16922                 
16923                 labelCfg = cfg.cn[0];
16924                 contentCfg = cfg.cn[1];
16925             
16926             }
16927             
16928             if(this.labelWidth > 12){
16929                 labelCfg.style = "width: " + this.labelWidth + 'px';
16930             }
16931             if(this.width * 1 > 0){
16932                 contentCfg.style = "width: " + this.width + 'px';
16933             }
16934             if(this.labelWidth < 13 && this.labelmd == 0){
16935                 this.labelmd = this.labelWidth;
16936             }
16937             
16938             if(this.labellg > 0){
16939                 labelCfg.cls += ' col-lg-' + this.labellg;
16940                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16941             }
16942             
16943             if(this.labelmd > 0){
16944                 labelCfg.cls += ' col-md-' + this.labelmd;
16945                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16946             }
16947             
16948             if(this.labelsm > 0){
16949                 labelCfg.cls += ' col-sm-' + this.labelsm;
16950                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16951             }
16952             
16953             if(this.labelxs > 0){
16954                 labelCfg.cls += ' col-xs-' + this.labelxs;
16955                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16956             }
16957                 
16958                 
16959         } else if ( this.fieldLabel.length) {
16960 //                Roo.log(" label");
16961                  cfg.cn = [
16962                    indicator,
16963                     {
16964                         tag: 'label',
16965                         //cls : 'input-group-addon',
16966                         html : this.fieldLabel
16967                     },
16968                     combobox
16969                 ];
16970                 
16971                 if(this.indicatorpos == 'right'){
16972                     cfg.cn = [
16973                         {
16974                             tag: 'label',
16975                             //cls : 'input-group-addon',
16976                             html : this.fieldLabel
16977                         },
16978                         indicator,
16979                         combobox
16980                     ];
16981                     
16982                 }
16983
16984         } else {
16985             
16986 //                Roo.log(" no label && no align");
16987                 cfg = combobox
16988                      
16989                 
16990         }
16991          
16992         var settings=this;
16993         ['xs','sm','md','lg'].map(function(size){
16994             if (settings[size]) {
16995                 cfg.cls += ' col-' + size + '-' + settings[size];
16996             }
16997         });
16998         
16999         return cfg;
17000         
17001     },
17002     
17003     _initEventsCalled : false,
17004     
17005     // private
17006     initEvents: function()
17007     {   
17008         if (this._initEventsCalled) { // as we call render... prevent looping...
17009             return;
17010         }
17011         this._initEventsCalled = true;
17012         
17013         if (!this.store) {
17014             throw "can not find store for combo";
17015         }
17016         
17017         this.indicator = this.indicatorEl();
17018         
17019         this.store = Roo.factory(this.store, Roo.data);
17020         this.store.parent = this;
17021         
17022         // if we are building from html. then this element is so complex, that we can not really
17023         // use the rendered HTML.
17024         // so we have to trash and replace the previous code.
17025         if (Roo.XComponent.build_from_html) {
17026             // remove this element....
17027             var e = this.el.dom, k=0;
17028             while (e ) { e = e.previousSibling;  ++k;}
17029
17030             this.el.remove();
17031             
17032             this.el=false;
17033             this.rendered = false;
17034             
17035             this.render(this.parent().getChildContainer(true), k);
17036         }
17037         
17038         if(Roo.isIOS && this.useNativeIOS){
17039             this.initIOSView();
17040             return;
17041         }
17042         
17043         /*
17044          * Touch Devices
17045          */
17046         
17047         if(Roo.isTouch && this.mobileTouchView){
17048             this.initTouchView();
17049             return;
17050         }
17051         
17052         if(this.tickable){
17053             this.initTickableEvents();
17054             return;
17055         }
17056         
17057         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17058         
17059         if(this.hiddenName){
17060             
17061             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17062             
17063             this.hiddenField.dom.value =
17064                 this.hiddenValue !== undefined ? this.hiddenValue :
17065                 this.value !== undefined ? this.value : '';
17066
17067             // prevent input submission
17068             this.el.dom.removeAttribute('name');
17069             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17070              
17071              
17072         }
17073         //if(Roo.isGecko){
17074         //    this.el.dom.setAttribute('autocomplete', 'off');
17075         //}
17076         
17077         var cls = 'x-combo-list';
17078         
17079         //this.list = new Roo.Layer({
17080         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17081         //});
17082         
17083         var _this = this;
17084         
17085         (function(){
17086             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17087             _this.list.setWidth(lw);
17088         }).defer(100);
17089         
17090         this.list.on('mouseover', this.onViewOver, this);
17091         this.list.on('mousemove', this.onViewMove, this);
17092         this.list.on('scroll', this.onViewScroll, this);
17093         
17094         /*
17095         this.list.swallowEvent('mousewheel');
17096         this.assetHeight = 0;
17097
17098         if(this.title){
17099             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17100             this.assetHeight += this.header.getHeight();
17101         }
17102
17103         this.innerList = this.list.createChild({cls:cls+'-inner'});
17104         this.innerList.on('mouseover', this.onViewOver, this);
17105         this.innerList.on('mousemove', this.onViewMove, this);
17106         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17107         
17108         if(this.allowBlank && !this.pageSize && !this.disableClear){
17109             this.footer = this.list.createChild({cls:cls+'-ft'});
17110             this.pageTb = new Roo.Toolbar(this.footer);
17111            
17112         }
17113         if(this.pageSize){
17114             this.footer = this.list.createChild({cls:cls+'-ft'});
17115             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17116                     {pageSize: this.pageSize});
17117             
17118         }
17119         
17120         if (this.pageTb && this.allowBlank && !this.disableClear) {
17121             var _this = this;
17122             this.pageTb.add(new Roo.Toolbar.Fill(), {
17123                 cls: 'x-btn-icon x-btn-clear',
17124                 text: '&#160;',
17125                 handler: function()
17126                 {
17127                     _this.collapse();
17128                     _this.clearValue();
17129                     _this.onSelect(false, -1);
17130                 }
17131             });
17132         }
17133         if (this.footer) {
17134             this.assetHeight += this.footer.getHeight();
17135         }
17136         */
17137             
17138         if(!this.tpl){
17139             this.tpl = Roo.bootstrap.version == 4 ?
17140                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17141                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17142         }
17143
17144         this.view = new Roo.View(this.list, this.tpl, {
17145             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17146         });
17147         //this.view.wrapEl.setDisplayed(false);
17148         this.view.on('click', this.onViewClick, this);
17149         
17150         
17151         this.store.on('beforeload', this.onBeforeLoad, this);
17152         this.store.on('load', this.onLoad, this);
17153         this.store.on('loadexception', this.onLoadException, this);
17154         /*
17155         if(this.resizable){
17156             this.resizer = new Roo.Resizable(this.list,  {
17157                pinned:true, handles:'se'
17158             });
17159             this.resizer.on('resize', function(r, w, h){
17160                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17161                 this.listWidth = w;
17162                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17163                 this.restrictHeight();
17164             }, this);
17165             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17166         }
17167         */
17168         if(!this.editable){
17169             this.editable = true;
17170             this.setEditable(false);
17171         }
17172         
17173         /*
17174         
17175         if (typeof(this.events.add.listeners) != 'undefined') {
17176             
17177             this.addicon = this.wrap.createChild(
17178                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17179        
17180             this.addicon.on('click', function(e) {
17181                 this.fireEvent('add', this);
17182             }, this);
17183         }
17184         if (typeof(this.events.edit.listeners) != 'undefined') {
17185             
17186             this.editicon = this.wrap.createChild(
17187                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17188             if (this.addicon) {
17189                 this.editicon.setStyle('margin-left', '40px');
17190             }
17191             this.editicon.on('click', function(e) {
17192                 
17193                 // we fire even  if inothing is selected..
17194                 this.fireEvent('edit', this, this.lastData );
17195                 
17196             }, this);
17197         }
17198         */
17199         
17200         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17201             "up" : function(e){
17202                 this.inKeyMode = true;
17203                 this.selectPrev();
17204             },
17205
17206             "down" : function(e){
17207                 if(!this.isExpanded()){
17208                     this.onTriggerClick();
17209                 }else{
17210                     this.inKeyMode = true;
17211                     this.selectNext();
17212                 }
17213             },
17214
17215             "enter" : function(e){
17216 //                this.onViewClick();
17217                 //return true;
17218                 this.collapse();
17219                 
17220                 if(this.fireEvent("specialkey", this, e)){
17221                     this.onViewClick(false);
17222                 }
17223                 
17224                 return true;
17225             },
17226
17227             "esc" : function(e){
17228                 this.collapse();
17229             },
17230
17231             "tab" : function(e){
17232                 this.collapse();
17233                 
17234                 if(this.fireEvent("specialkey", this, e)){
17235                     this.onViewClick(false);
17236                 }
17237                 
17238                 return true;
17239             },
17240
17241             scope : this,
17242
17243             doRelay : function(foo, bar, hname){
17244                 if(hname == 'down' || this.scope.isExpanded()){
17245                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17246                 }
17247                 return true;
17248             },
17249
17250             forceKeyDown: true
17251         });
17252         
17253         
17254         this.queryDelay = Math.max(this.queryDelay || 10,
17255                 this.mode == 'local' ? 10 : 250);
17256         
17257         
17258         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17259         
17260         if(this.typeAhead){
17261             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17262         }
17263         if(this.editable !== false){
17264             this.inputEl().on("keyup", this.onKeyUp, this);
17265         }
17266         if(this.forceSelection){
17267             this.inputEl().on('blur', this.doForce, this);
17268         }
17269         
17270         if(this.multiple){
17271             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17272             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17273         }
17274     },
17275     
17276     initTickableEvents: function()
17277     {   
17278         this.createList();
17279         
17280         if(this.hiddenName){
17281             
17282             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17283             
17284             this.hiddenField.dom.value =
17285                 this.hiddenValue !== undefined ? this.hiddenValue :
17286                 this.value !== undefined ? this.value : '';
17287
17288             // prevent input submission
17289             this.el.dom.removeAttribute('name');
17290             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17291              
17292              
17293         }
17294         
17295 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17296         
17297         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17298         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17299         if(this.triggerList){
17300             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17301         }
17302          
17303         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17304         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17305         
17306         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17307         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17308         
17309         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17310         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17311         
17312         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17313         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17314         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17315         
17316         this.okBtn.hide();
17317         this.cancelBtn.hide();
17318         
17319         var _this = this;
17320         
17321         (function(){
17322             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17323             _this.list.setWidth(lw);
17324         }).defer(100);
17325         
17326         this.list.on('mouseover', this.onViewOver, this);
17327         this.list.on('mousemove', this.onViewMove, this);
17328         
17329         this.list.on('scroll', this.onViewScroll, this);
17330         
17331         if(!this.tpl){
17332             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17333                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17334         }
17335
17336         this.view = new Roo.View(this.list, this.tpl, {
17337             singleSelect:true,
17338             tickable:true,
17339             parent:this,
17340             store: this.store,
17341             selectedClass: this.selectedClass
17342         });
17343         
17344         //this.view.wrapEl.setDisplayed(false);
17345         this.view.on('click', this.onViewClick, this);
17346         
17347         
17348         
17349         this.store.on('beforeload', this.onBeforeLoad, this);
17350         this.store.on('load', this.onLoad, this);
17351         this.store.on('loadexception', this.onLoadException, this);
17352         
17353         if(this.editable){
17354             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17355                 "up" : function(e){
17356                     this.inKeyMode = true;
17357                     this.selectPrev();
17358                 },
17359
17360                 "down" : function(e){
17361                     this.inKeyMode = true;
17362                     this.selectNext();
17363                 },
17364
17365                 "enter" : function(e){
17366                     if(this.fireEvent("specialkey", this, e)){
17367                         this.onViewClick(false);
17368                     }
17369                     
17370                     return true;
17371                 },
17372
17373                 "esc" : function(e){
17374                     this.onTickableFooterButtonClick(e, false, false);
17375                 },
17376
17377                 "tab" : function(e){
17378                     this.fireEvent("specialkey", this, e);
17379                     
17380                     this.onTickableFooterButtonClick(e, false, false);
17381                     
17382                     return true;
17383                 },
17384
17385                 scope : this,
17386
17387                 doRelay : function(e, fn, key){
17388                     if(this.scope.isExpanded()){
17389                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17390                     }
17391                     return true;
17392                 },
17393
17394                 forceKeyDown: true
17395             });
17396         }
17397         
17398         this.queryDelay = Math.max(this.queryDelay || 10,
17399                 this.mode == 'local' ? 10 : 250);
17400         
17401         
17402         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17403         
17404         if(this.typeAhead){
17405             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17406         }
17407         
17408         if(this.editable !== false){
17409             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17410         }
17411         
17412         this.indicator = this.indicatorEl();
17413         
17414         if(this.indicator){
17415             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17416             this.indicator.hide();
17417         }
17418         
17419     },
17420
17421     onDestroy : function(){
17422         if(this.view){
17423             this.view.setStore(null);
17424             this.view.el.removeAllListeners();
17425             this.view.el.remove();
17426             this.view.purgeListeners();
17427         }
17428         if(this.list){
17429             this.list.dom.innerHTML  = '';
17430         }
17431         
17432         if(this.store){
17433             this.store.un('beforeload', this.onBeforeLoad, this);
17434             this.store.un('load', this.onLoad, this);
17435             this.store.un('loadexception', this.onLoadException, this);
17436         }
17437         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17438     },
17439
17440     // private
17441     fireKey : function(e){
17442         if(e.isNavKeyPress() && !this.list.isVisible()){
17443             this.fireEvent("specialkey", this, e);
17444         }
17445     },
17446
17447     // private
17448     onResize: function(w, h)
17449     {
17450         
17451         
17452 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17453 //        
17454 //        if(typeof w != 'number'){
17455 //            // we do not handle it!?!?
17456 //            return;
17457 //        }
17458 //        var tw = this.trigger.getWidth();
17459 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17460 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17461 //        var x = w - tw;
17462 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17463 //            
17464 //        //this.trigger.setStyle('left', x+'px');
17465 //        
17466 //        if(this.list && this.listWidth === undefined){
17467 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17468 //            this.list.setWidth(lw);
17469 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17470 //        }
17471         
17472     
17473         
17474     },
17475
17476     /**
17477      * Allow or prevent the user from directly editing the field text.  If false is passed,
17478      * the user will only be able to select from the items defined in the dropdown list.  This method
17479      * is the runtime equivalent of setting the 'editable' config option at config time.
17480      * @param {Boolean} value True to allow the user to directly edit the field text
17481      */
17482     setEditable : function(value){
17483         if(value == this.editable){
17484             return;
17485         }
17486         this.editable = value;
17487         if(!value){
17488             this.inputEl().dom.setAttribute('readOnly', true);
17489             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17490             this.inputEl().addClass('x-combo-noedit');
17491         }else{
17492             this.inputEl().dom.removeAttribute('readOnly');
17493             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17494             this.inputEl().removeClass('x-combo-noedit');
17495         }
17496     },
17497
17498     // private
17499     
17500     onBeforeLoad : function(combo,opts){
17501         if(!this.hasFocus){
17502             return;
17503         }
17504          if (!opts.add) {
17505             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17506          }
17507         this.restrictHeight();
17508         this.selectedIndex = -1;
17509     },
17510
17511     // private
17512     onLoad : function(){
17513         
17514         this.hasQuery = false;
17515         
17516         if(!this.hasFocus){
17517             return;
17518         }
17519         
17520         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17521             this.loading.hide();
17522         }
17523         
17524         if(this.store.getCount() > 0){
17525             
17526             this.expand();
17527             this.restrictHeight();
17528             if(this.lastQuery == this.allQuery){
17529                 if(this.editable && !this.tickable){
17530                     this.inputEl().dom.select();
17531                 }
17532                 
17533                 if(
17534                     !this.selectByValue(this.value, true) &&
17535                     this.autoFocus && 
17536                     (
17537                         !this.store.lastOptions ||
17538                         typeof(this.store.lastOptions.add) == 'undefined' || 
17539                         this.store.lastOptions.add != true
17540                     )
17541                 ){
17542                     this.select(0, true);
17543                 }
17544             }else{
17545                 if(this.autoFocus){
17546                     this.selectNext();
17547                 }
17548                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17549                     this.taTask.delay(this.typeAheadDelay);
17550                 }
17551             }
17552         }else{
17553             this.onEmptyResults();
17554         }
17555         
17556         //this.el.focus();
17557     },
17558     // private
17559     onLoadException : function()
17560     {
17561         this.hasQuery = false;
17562         
17563         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17564             this.loading.hide();
17565         }
17566         
17567         if(this.tickable && this.editable){
17568             return;
17569         }
17570         
17571         this.collapse();
17572         // only causes errors at present
17573         //Roo.log(this.store.reader.jsonData);
17574         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17575             // fixme
17576             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17577         //}
17578         
17579         
17580     },
17581     // private
17582     onTypeAhead : function(){
17583         if(this.store.getCount() > 0){
17584             var r = this.store.getAt(0);
17585             var newValue = r.data[this.displayField];
17586             var len = newValue.length;
17587             var selStart = this.getRawValue().length;
17588             
17589             if(selStart != len){
17590                 this.setRawValue(newValue);
17591                 this.selectText(selStart, newValue.length);
17592             }
17593         }
17594     },
17595
17596     // private
17597     onSelect : function(record, index){
17598         
17599         if(this.fireEvent('beforeselect', this, record, index) !== false){
17600         
17601             this.setFromData(index > -1 ? record.data : false);
17602             
17603             this.collapse();
17604             this.fireEvent('select', this, record, index);
17605         }
17606     },
17607
17608     /**
17609      * Returns the currently selected field value or empty string if no value is set.
17610      * @return {String} value The selected value
17611      */
17612     getValue : function()
17613     {
17614         if(Roo.isIOS && this.useNativeIOS){
17615             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17616         }
17617         
17618         if(this.multiple){
17619             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17620         }
17621         
17622         if(this.valueField){
17623             return typeof this.value != 'undefined' ? this.value : '';
17624         }else{
17625             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17626         }
17627     },
17628     
17629     getRawValue : function()
17630     {
17631         if(Roo.isIOS && this.useNativeIOS){
17632             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17633         }
17634         
17635         var v = this.inputEl().getValue();
17636         
17637         return v;
17638     },
17639
17640     /**
17641      * Clears any text/value currently set in the field
17642      */
17643     clearValue : function(){
17644         
17645         if(this.hiddenField){
17646             this.hiddenField.dom.value = '';
17647         }
17648         this.value = '';
17649         this.setRawValue('');
17650         this.lastSelectionText = '';
17651         this.lastData = false;
17652         
17653         var close = this.closeTriggerEl();
17654         
17655         if(close){
17656             close.hide();
17657         }
17658         
17659         this.validate();
17660         
17661     },
17662
17663     /**
17664      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17665      * will be displayed in the field.  If the value does not match the data value of an existing item,
17666      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17667      * Otherwise the field will be blank (although the value will still be set).
17668      * @param {String} value The value to match
17669      */
17670     setValue : function(v)
17671     {
17672         if(Roo.isIOS && this.useNativeIOS){
17673             this.setIOSValue(v);
17674             return;
17675         }
17676         
17677         if(this.multiple){
17678             this.syncValue();
17679             return;
17680         }
17681         
17682         var text = v;
17683         if(this.valueField){
17684             var r = this.findRecord(this.valueField, v);
17685             if(r){
17686                 text = r.data[this.displayField];
17687             }else if(this.valueNotFoundText !== undefined){
17688                 text = this.valueNotFoundText;
17689             }
17690         }
17691         this.lastSelectionText = text;
17692         if(this.hiddenField){
17693             this.hiddenField.dom.value = v;
17694         }
17695         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17696         this.value = v;
17697         
17698         var close = this.closeTriggerEl();
17699         
17700         if(close){
17701             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17702         }
17703         
17704         this.validate();
17705     },
17706     /**
17707      * @property {Object} the last set data for the element
17708      */
17709     
17710     lastData : false,
17711     /**
17712      * Sets the value of the field based on a object which is related to the record format for the store.
17713      * @param {Object} value the value to set as. or false on reset?
17714      */
17715     setFromData : function(o){
17716         
17717         if(this.multiple){
17718             this.addItem(o);
17719             return;
17720         }
17721             
17722         var dv = ''; // display value
17723         var vv = ''; // value value..
17724         this.lastData = o;
17725         if (this.displayField) {
17726             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17727         } else {
17728             // this is an error condition!!!
17729             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17730         }
17731         
17732         if(this.valueField){
17733             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17734         }
17735         
17736         var close = this.closeTriggerEl();
17737         
17738         if(close){
17739             if(dv.length || vv * 1 > 0){
17740                 close.show() ;
17741                 this.blockFocus=true;
17742             } else {
17743                 close.hide();
17744             }             
17745         }
17746         
17747         if(this.hiddenField){
17748             this.hiddenField.dom.value = vv;
17749             
17750             this.lastSelectionText = dv;
17751             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17752             this.value = vv;
17753             return;
17754         }
17755         // no hidden field.. - we store the value in 'value', but still display
17756         // display field!!!!
17757         this.lastSelectionText = dv;
17758         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17759         this.value = vv;
17760         
17761         
17762         
17763     },
17764     // private
17765     reset : function(){
17766         // overridden so that last data is reset..
17767         
17768         if(this.multiple){
17769             this.clearItem();
17770             return;
17771         }
17772         
17773         this.setValue(this.originalValue);
17774         //this.clearInvalid();
17775         this.lastData = false;
17776         if (this.view) {
17777             this.view.clearSelections();
17778         }
17779         
17780         this.validate();
17781     },
17782     // private
17783     findRecord : function(prop, value){
17784         var record;
17785         if(this.store.getCount() > 0){
17786             this.store.each(function(r){
17787                 if(r.data[prop] == value){
17788                     record = r;
17789                     return false;
17790                 }
17791                 return true;
17792             });
17793         }
17794         return record;
17795     },
17796     
17797     getName: function()
17798     {
17799         // returns hidden if it's set..
17800         if (!this.rendered) {return ''};
17801         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17802         
17803     },
17804     // private
17805     onViewMove : function(e, t){
17806         this.inKeyMode = false;
17807     },
17808
17809     // private
17810     onViewOver : function(e, t){
17811         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17812             return;
17813         }
17814         var item = this.view.findItemFromChild(t);
17815         
17816         if(item){
17817             var index = this.view.indexOf(item);
17818             this.select(index, false);
17819         }
17820     },
17821
17822     // private
17823     onViewClick : function(view, doFocus, el, e)
17824     {
17825         var index = this.view.getSelectedIndexes()[0];
17826         
17827         var r = this.store.getAt(index);
17828         
17829         if(this.tickable){
17830             
17831             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17832                 return;
17833             }
17834             
17835             var rm = false;
17836             var _this = this;
17837             
17838             Roo.each(this.tickItems, function(v,k){
17839                 
17840                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17841                     Roo.log(v);
17842                     _this.tickItems.splice(k, 1);
17843                     
17844                     if(typeof(e) == 'undefined' && view == false){
17845                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17846                     }
17847                     
17848                     rm = true;
17849                     return;
17850                 }
17851             });
17852             
17853             if(rm){
17854                 return;
17855             }
17856             
17857             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17858                 this.tickItems.push(r.data);
17859             }
17860             
17861             if(typeof(e) == 'undefined' && view == false){
17862                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17863             }
17864                     
17865             return;
17866         }
17867         
17868         if(r){
17869             this.onSelect(r, index);
17870         }
17871         if(doFocus !== false && !this.blockFocus){
17872             this.inputEl().focus();
17873         }
17874     },
17875
17876     // private
17877     restrictHeight : function(){
17878         //this.innerList.dom.style.height = '';
17879         //var inner = this.innerList.dom;
17880         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17881         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17882         //this.list.beginUpdate();
17883         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17884         this.list.alignTo(this.inputEl(), this.listAlign);
17885         this.list.alignTo(this.inputEl(), this.listAlign);
17886         //this.list.endUpdate();
17887     },
17888
17889     // private
17890     onEmptyResults : function(){
17891         
17892         if(this.tickable && this.editable){
17893             this.hasFocus = false;
17894             this.restrictHeight();
17895             return;
17896         }
17897         
17898         this.collapse();
17899     },
17900
17901     /**
17902      * Returns true if the dropdown list is expanded, else false.
17903      */
17904     isExpanded : function(){
17905         return this.list.isVisible();
17906     },
17907
17908     /**
17909      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17910      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17911      * @param {String} value The data value of the item to select
17912      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17913      * selected item if it is not currently in view (defaults to true)
17914      * @return {Boolean} True if the value matched an item in the list, else false
17915      */
17916     selectByValue : function(v, scrollIntoView){
17917         if(v !== undefined && v !== null){
17918             var r = this.findRecord(this.valueField || this.displayField, v);
17919             if(r){
17920                 this.select(this.store.indexOf(r), scrollIntoView);
17921                 return true;
17922             }
17923         }
17924         return false;
17925     },
17926
17927     /**
17928      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17929      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17930      * @param {Number} index The zero-based index of the list item to select
17931      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17932      * selected item if it is not currently in view (defaults to true)
17933      */
17934     select : function(index, scrollIntoView){
17935         this.selectedIndex = index;
17936         this.view.select(index);
17937         if(scrollIntoView !== false){
17938             var el = this.view.getNode(index);
17939             /*
17940              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17941              */
17942             if(el){
17943                 this.list.scrollChildIntoView(el, false);
17944             }
17945         }
17946     },
17947
17948     // private
17949     selectNext : function(){
17950         var ct = this.store.getCount();
17951         if(ct > 0){
17952             if(this.selectedIndex == -1){
17953                 this.select(0);
17954             }else if(this.selectedIndex < ct-1){
17955                 this.select(this.selectedIndex+1);
17956             }
17957         }
17958     },
17959
17960     // private
17961     selectPrev : function(){
17962         var ct = this.store.getCount();
17963         if(ct > 0){
17964             if(this.selectedIndex == -1){
17965                 this.select(0);
17966             }else if(this.selectedIndex != 0){
17967                 this.select(this.selectedIndex-1);
17968             }
17969         }
17970     },
17971
17972     // private
17973     onKeyUp : function(e){
17974         if(this.editable !== false && !e.isSpecialKey()){
17975             this.lastKey = e.getKey();
17976             this.dqTask.delay(this.queryDelay);
17977         }
17978     },
17979
17980     // private
17981     validateBlur : function(){
17982         return !this.list || !this.list.isVisible();   
17983     },
17984
17985     // private
17986     initQuery : function(){
17987         
17988         var v = this.getRawValue();
17989         
17990         if(this.tickable && this.editable){
17991             v = this.tickableInputEl().getValue();
17992         }
17993         
17994         this.doQuery(v);
17995     },
17996
17997     // private
17998     doForce : function(){
17999         if(this.inputEl().dom.value.length > 0){
18000             this.inputEl().dom.value =
18001                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18002              
18003         }
18004     },
18005
18006     /**
18007      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18008      * query allowing the query action to be canceled if needed.
18009      * @param {String} query The SQL query to execute
18010      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18011      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18012      * saved in the current store (defaults to false)
18013      */
18014     doQuery : function(q, forceAll){
18015         
18016         if(q === undefined || q === null){
18017             q = '';
18018         }
18019         var qe = {
18020             query: q,
18021             forceAll: forceAll,
18022             combo: this,
18023             cancel:false
18024         };
18025         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18026             return false;
18027         }
18028         q = qe.query;
18029         
18030         forceAll = qe.forceAll;
18031         if(forceAll === true || (q.length >= this.minChars)){
18032             
18033             this.hasQuery = true;
18034             
18035             if(this.lastQuery != q || this.alwaysQuery){
18036                 this.lastQuery = q;
18037                 if(this.mode == 'local'){
18038                     this.selectedIndex = -1;
18039                     if(forceAll){
18040                         this.store.clearFilter();
18041                     }else{
18042                         
18043                         if(this.specialFilter){
18044                             this.fireEvent('specialfilter', this);
18045                             this.onLoad();
18046                             return;
18047                         }
18048                         
18049                         this.store.filter(this.displayField, q);
18050                     }
18051                     
18052                     this.store.fireEvent("datachanged", this.store);
18053                     
18054                     this.onLoad();
18055                     
18056                     
18057                 }else{
18058                     
18059                     this.store.baseParams[this.queryParam] = q;
18060                     
18061                     var options = {params : this.getParams(q)};
18062                     
18063                     if(this.loadNext){
18064                         options.add = true;
18065                         options.params.start = this.page * this.pageSize;
18066                     }
18067                     
18068                     this.store.load(options);
18069                     
18070                     /*
18071                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18072                      *  we should expand the list on onLoad
18073                      *  so command out it
18074                      */
18075 //                    this.expand();
18076                 }
18077             }else{
18078                 this.selectedIndex = -1;
18079                 this.onLoad();   
18080             }
18081         }
18082         
18083         this.loadNext = false;
18084     },
18085     
18086     // private
18087     getParams : function(q){
18088         var p = {};
18089         //p[this.queryParam] = q;
18090         
18091         if(this.pageSize){
18092             p.start = 0;
18093             p.limit = this.pageSize;
18094         }
18095         return p;
18096     },
18097
18098     /**
18099      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18100      */
18101     collapse : function(){
18102         if(!this.isExpanded()){
18103             return;
18104         }
18105         
18106         this.list.hide();
18107         
18108         this.hasFocus = false;
18109         
18110         if(this.tickable){
18111             this.okBtn.hide();
18112             this.cancelBtn.hide();
18113             this.trigger.show();
18114             
18115             if(this.editable){
18116                 this.tickableInputEl().dom.value = '';
18117                 this.tickableInputEl().blur();
18118             }
18119             
18120         }
18121         
18122         Roo.get(document).un('mousedown', this.collapseIf, this);
18123         Roo.get(document).un('mousewheel', this.collapseIf, this);
18124         if (!this.editable) {
18125             Roo.get(document).un('keydown', this.listKeyPress, this);
18126         }
18127         this.fireEvent('collapse', this);
18128         
18129         this.validate();
18130     },
18131
18132     // private
18133     collapseIf : function(e){
18134         var in_combo  = e.within(this.el);
18135         var in_list =  e.within(this.list);
18136         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18137         
18138         if (in_combo || in_list || is_list) {
18139             //e.stopPropagation();
18140             return;
18141         }
18142         
18143         if(this.tickable){
18144             this.onTickableFooterButtonClick(e, false, false);
18145         }
18146
18147         this.collapse();
18148         
18149     },
18150
18151     /**
18152      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18153      */
18154     expand : function(){
18155        
18156         if(this.isExpanded() || !this.hasFocus){
18157             return;
18158         }
18159         
18160         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18161         this.list.setWidth(lw);
18162         
18163         Roo.log('expand');
18164         
18165         this.list.show();
18166         
18167         this.restrictHeight();
18168         
18169         if(this.tickable){
18170             
18171             this.tickItems = Roo.apply([], this.item);
18172             
18173             this.okBtn.show();
18174             this.cancelBtn.show();
18175             this.trigger.hide();
18176             
18177             if(this.editable){
18178                 this.tickableInputEl().focus();
18179             }
18180             
18181         }
18182         
18183         Roo.get(document).on('mousedown', this.collapseIf, this);
18184         Roo.get(document).on('mousewheel', this.collapseIf, this);
18185         if (!this.editable) {
18186             Roo.get(document).on('keydown', this.listKeyPress, this);
18187         }
18188         
18189         this.fireEvent('expand', this);
18190     },
18191
18192     // private
18193     // Implements the default empty TriggerField.onTriggerClick function
18194     onTriggerClick : function(e)
18195     {
18196         Roo.log('trigger click');
18197         
18198         if(this.disabled || !this.triggerList){
18199             return;
18200         }
18201         
18202         this.page = 0;
18203         this.loadNext = false;
18204         
18205         if(this.isExpanded()){
18206             this.collapse();
18207             if (!this.blockFocus) {
18208                 this.inputEl().focus();
18209             }
18210             
18211         }else {
18212             this.hasFocus = true;
18213             if(this.triggerAction == 'all') {
18214                 this.doQuery(this.allQuery, true);
18215             } else {
18216                 this.doQuery(this.getRawValue());
18217             }
18218             if (!this.blockFocus) {
18219                 this.inputEl().focus();
18220             }
18221         }
18222     },
18223     
18224     onTickableTriggerClick : function(e)
18225     {
18226         if(this.disabled){
18227             return;
18228         }
18229         
18230         this.page = 0;
18231         this.loadNext = false;
18232         this.hasFocus = true;
18233         
18234         if(this.triggerAction == 'all') {
18235             this.doQuery(this.allQuery, true);
18236         } else {
18237             this.doQuery(this.getRawValue());
18238         }
18239     },
18240     
18241     onSearchFieldClick : function(e)
18242     {
18243         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18244             this.onTickableFooterButtonClick(e, false, false);
18245             return;
18246         }
18247         
18248         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18249             return;
18250         }
18251         
18252         this.page = 0;
18253         this.loadNext = false;
18254         this.hasFocus = true;
18255         
18256         if(this.triggerAction == 'all') {
18257             this.doQuery(this.allQuery, true);
18258         } else {
18259             this.doQuery(this.getRawValue());
18260         }
18261     },
18262     
18263     listKeyPress : function(e)
18264     {
18265         //Roo.log('listkeypress');
18266         // scroll to first matching element based on key pres..
18267         if (e.isSpecialKey()) {
18268             return false;
18269         }
18270         var k = String.fromCharCode(e.getKey()).toUpperCase();
18271         //Roo.log(k);
18272         var match  = false;
18273         var csel = this.view.getSelectedNodes();
18274         var cselitem = false;
18275         if (csel.length) {
18276             var ix = this.view.indexOf(csel[0]);
18277             cselitem  = this.store.getAt(ix);
18278             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18279                 cselitem = false;
18280             }
18281             
18282         }
18283         
18284         this.store.each(function(v) { 
18285             if (cselitem) {
18286                 // start at existing selection.
18287                 if (cselitem.id == v.id) {
18288                     cselitem = false;
18289                 }
18290                 return true;
18291             }
18292                 
18293             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18294                 match = this.store.indexOf(v);
18295                 return false;
18296             }
18297             return true;
18298         }, this);
18299         
18300         if (match === false) {
18301             return true; // no more action?
18302         }
18303         // scroll to?
18304         this.view.select(match);
18305         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18306         sn.scrollIntoView(sn.dom.parentNode, false);
18307     },
18308     
18309     onViewScroll : function(e, t){
18310         
18311         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
18312             return;
18313         }
18314         
18315         this.hasQuery = true;
18316         
18317         this.loading = this.list.select('.loading', true).first();
18318         
18319         if(this.loading === null){
18320             this.list.createChild({
18321                 tag: 'div',
18322                 cls: 'loading roo-select2-more-results roo-select2-active',
18323                 html: 'Loading more results...'
18324             });
18325             
18326             this.loading = this.list.select('.loading', true).first();
18327             
18328             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18329             
18330             this.loading.hide();
18331         }
18332         
18333         this.loading.show();
18334         
18335         var _combo = this;
18336         
18337         this.page++;
18338         this.loadNext = true;
18339         
18340         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18341         
18342         return;
18343     },
18344     
18345     addItem : function(o)
18346     {   
18347         var dv = ''; // display value
18348         
18349         if (this.displayField) {
18350             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18351         } else {
18352             // this is an error condition!!!
18353             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18354         }
18355         
18356         if(!dv.length){
18357             return;
18358         }
18359         
18360         var choice = this.choices.createChild({
18361             tag: 'li',
18362             cls: 'roo-select2-search-choice',
18363             cn: [
18364                 {
18365                     tag: 'div',
18366                     html: dv
18367                 },
18368                 {
18369                     tag: 'a',
18370                     href: '#',
18371                     cls: 'roo-select2-search-choice-close fa fa-times',
18372                     tabindex: '-1'
18373                 }
18374             ]
18375             
18376         }, this.searchField);
18377         
18378         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18379         
18380         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18381         
18382         this.item.push(o);
18383         
18384         this.lastData = o;
18385         
18386         this.syncValue();
18387         
18388         this.inputEl().dom.value = '';
18389         
18390         this.validate();
18391     },
18392     
18393     onRemoveItem : function(e, _self, o)
18394     {
18395         e.preventDefault();
18396         
18397         this.lastItem = Roo.apply([], this.item);
18398         
18399         var index = this.item.indexOf(o.data) * 1;
18400         
18401         if( index < 0){
18402             Roo.log('not this item?!');
18403             return;
18404         }
18405         
18406         this.item.splice(index, 1);
18407         o.item.remove();
18408         
18409         this.syncValue();
18410         
18411         this.fireEvent('remove', this, e);
18412         
18413         this.validate();
18414         
18415     },
18416     
18417     syncValue : function()
18418     {
18419         if(!this.item.length){
18420             this.clearValue();
18421             return;
18422         }
18423             
18424         var value = [];
18425         var _this = this;
18426         Roo.each(this.item, function(i){
18427             if(_this.valueField){
18428                 value.push(i[_this.valueField]);
18429                 return;
18430             }
18431
18432             value.push(i);
18433         });
18434
18435         this.value = value.join(',');
18436
18437         if(this.hiddenField){
18438             this.hiddenField.dom.value = this.value;
18439         }
18440         
18441         this.store.fireEvent("datachanged", this.store);
18442         
18443         this.validate();
18444     },
18445     
18446     clearItem : function()
18447     {
18448         if(!this.multiple){
18449             return;
18450         }
18451         
18452         this.item = [];
18453         
18454         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18455            c.remove();
18456         });
18457         
18458         this.syncValue();
18459         
18460         this.validate();
18461         
18462         if(this.tickable && !Roo.isTouch){
18463             this.view.refresh();
18464         }
18465     },
18466     
18467     inputEl: function ()
18468     {
18469         if(Roo.isIOS && this.useNativeIOS){
18470             return this.el.select('select.roo-ios-select', true).first();
18471         }
18472         
18473         if(Roo.isTouch && this.mobileTouchView){
18474             return this.el.select('input.form-control',true).first();
18475         }
18476         
18477         if(this.tickable){
18478             return this.searchField;
18479         }
18480         
18481         return this.el.select('input.form-control',true).first();
18482     },
18483     
18484     onTickableFooterButtonClick : function(e, btn, el)
18485     {
18486         e.preventDefault();
18487         
18488         this.lastItem = Roo.apply([], this.item);
18489         
18490         if(btn && btn.name == 'cancel'){
18491             this.tickItems = Roo.apply([], this.item);
18492             this.collapse();
18493             return;
18494         }
18495         
18496         this.clearItem();
18497         
18498         var _this = this;
18499         
18500         Roo.each(this.tickItems, function(o){
18501             _this.addItem(o);
18502         });
18503         
18504         this.collapse();
18505         
18506     },
18507     
18508     validate : function()
18509     {
18510         if(this.getVisibilityEl().hasClass('hidden')){
18511             return true;
18512         }
18513         
18514         var v = this.getRawValue();
18515         
18516         if(this.multiple){
18517             v = this.getValue();
18518         }
18519         
18520         if(this.disabled || this.allowBlank || v.length){
18521             this.markValid();
18522             return true;
18523         }
18524         
18525         this.markInvalid();
18526         return false;
18527     },
18528     
18529     tickableInputEl : function()
18530     {
18531         if(!this.tickable || !this.editable){
18532             return this.inputEl();
18533         }
18534         
18535         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18536     },
18537     
18538     
18539     getAutoCreateTouchView : function()
18540     {
18541         var id = Roo.id();
18542         
18543         var cfg = {
18544             cls: 'form-group' //input-group
18545         };
18546         
18547         var input =  {
18548             tag: 'input',
18549             id : id,
18550             type : this.inputType,
18551             cls : 'form-control x-combo-noedit',
18552             autocomplete: 'new-password',
18553             placeholder : this.placeholder || '',
18554             readonly : true
18555         };
18556         
18557         if (this.name) {
18558             input.name = this.name;
18559         }
18560         
18561         if (this.size) {
18562             input.cls += ' input-' + this.size;
18563         }
18564         
18565         if (this.disabled) {
18566             input.disabled = true;
18567         }
18568         
18569         var inputblock = {
18570             cls : 'roo-combobox-wrap',
18571             cn : [
18572                 input
18573             ]
18574         };
18575         
18576         if(this.before){
18577             inputblock.cls += ' input-group';
18578             
18579             inputblock.cn.unshift({
18580                 tag :'span',
18581                 cls : 'input-group-addon input-group-prepend input-group-text',
18582                 html : this.before
18583             });
18584         }
18585         
18586         if(this.removable && !this.multiple){
18587             inputblock.cls += ' roo-removable';
18588             
18589             inputblock.cn.push({
18590                 tag: 'button',
18591                 html : 'x',
18592                 cls : 'roo-combo-removable-btn close'
18593             });
18594         }
18595
18596         if(this.hasFeedback && !this.allowBlank){
18597             
18598             inputblock.cls += ' has-feedback';
18599             
18600             inputblock.cn.push({
18601                 tag: 'span',
18602                 cls: 'glyphicon form-control-feedback'
18603             });
18604             
18605         }
18606         
18607         if (this.after) {
18608             
18609             inputblock.cls += (this.before) ? '' : ' input-group';
18610             
18611             inputblock.cn.push({
18612                 tag :'span',
18613                 cls : 'input-group-addon input-group-append input-group-text',
18614                 html : this.after
18615             });
18616         }
18617
18618         
18619         var ibwrap = inputblock;
18620         
18621         if(this.multiple){
18622             ibwrap = {
18623                 tag: 'ul',
18624                 cls: 'roo-select2-choices',
18625                 cn:[
18626                     {
18627                         tag: 'li',
18628                         cls: 'roo-select2-search-field',
18629                         cn: [
18630
18631                             inputblock
18632                         ]
18633                     }
18634                 ]
18635             };
18636         
18637             
18638         }
18639         
18640         var combobox = {
18641             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18642             cn: [
18643                 {
18644                     tag: 'input',
18645                     type : 'hidden',
18646                     cls: 'form-hidden-field'
18647                 },
18648                 ibwrap
18649             ]
18650         };
18651         
18652         if(!this.multiple && this.showToggleBtn){
18653             
18654             var caret = {
18655                 cls: 'caret'
18656             };
18657             
18658             if (this.caret != false) {
18659                 caret = {
18660                      tag: 'i',
18661                      cls: 'fa fa-' + this.caret
18662                 };
18663                 
18664             }
18665             
18666             combobox.cn.push({
18667                 tag :'span',
18668                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18669                 cn : [
18670                     Roo.bootstrap.version == 3 ? caret : '',
18671                     {
18672                         tag: 'span',
18673                         cls: 'combobox-clear',
18674                         cn  : [
18675                             {
18676                                 tag : 'i',
18677                                 cls: 'icon-remove'
18678                             }
18679                         ]
18680                     }
18681                 ]
18682
18683             })
18684         }
18685         
18686         if(this.multiple){
18687             combobox.cls += ' roo-select2-container-multi';
18688         }
18689         
18690         var required =  this.allowBlank ?  {
18691                     tag : 'i',
18692                     style: 'display: none'
18693                 } : {
18694                    tag : 'i',
18695                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18696                    tooltip : 'This field is required'
18697                 };
18698         
18699         var align = this.labelAlign || this.parentLabelAlign();
18700         
18701         if (align ==='left' && this.fieldLabel.length) {
18702
18703             cfg.cn = [
18704                 required,
18705                 {
18706                     tag: 'label',
18707                     cls : 'control-label col-form-label',
18708                     html : this.fieldLabel
18709
18710                 },
18711                 {
18712                     cls : 'roo-combobox-wrap ', 
18713                     cn: [
18714                         combobox
18715                     ]
18716                 }
18717             ];
18718             
18719             var labelCfg = cfg.cn[1];
18720             var contentCfg = cfg.cn[2];
18721             
18722
18723             if(this.indicatorpos == 'right'){
18724                 cfg.cn = [
18725                     {
18726                         tag: 'label',
18727                         'for' :  id,
18728                         cls : 'control-label col-form-label',
18729                         cn : [
18730                             {
18731                                 tag : 'span',
18732                                 html : this.fieldLabel
18733                             },
18734                             required
18735                         ]
18736                     },
18737                     {
18738                         cls : "roo-combobox-wrap ",
18739                         cn: [
18740                             combobox
18741                         ]
18742                     }
18743
18744                 ];
18745                 
18746                 labelCfg = cfg.cn[0];
18747                 contentCfg = cfg.cn[1];
18748             }
18749             
18750            
18751             
18752             if(this.labelWidth > 12){
18753                 labelCfg.style = "width: " + this.labelWidth + 'px';
18754             }
18755            
18756             if(this.labelWidth < 13 && this.labelmd == 0){
18757                 this.labelmd = this.labelWidth;
18758             }
18759             
18760             if(this.labellg > 0){
18761                 labelCfg.cls += ' col-lg-' + this.labellg;
18762                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18763             }
18764             
18765             if(this.labelmd > 0){
18766                 labelCfg.cls += ' col-md-' + this.labelmd;
18767                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18768             }
18769             
18770             if(this.labelsm > 0){
18771                 labelCfg.cls += ' col-sm-' + this.labelsm;
18772                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18773             }
18774             
18775             if(this.labelxs > 0){
18776                 labelCfg.cls += ' col-xs-' + this.labelxs;
18777                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18778             }
18779                 
18780                 
18781         } else if ( this.fieldLabel.length) {
18782             cfg.cn = [
18783                required,
18784                 {
18785                     tag: 'label',
18786                     cls : 'control-label',
18787                     html : this.fieldLabel
18788
18789                 },
18790                 {
18791                     cls : '', 
18792                     cn: [
18793                         combobox
18794                     ]
18795                 }
18796             ];
18797             
18798             if(this.indicatorpos == 'right'){
18799                 cfg.cn = [
18800                     {
18801                         tag: 'label',
18802                         cls : 'control-label',
18803                         html : this.fieldLabel,
18804                         cn : [
18805                             required
18806                         ]
18807                     },
18808                     {
18809                         cls : '', 
18810                         cn: [
18811                             combobox
18812                         ]
18813                     }
18814                 ];
18815             }
18816         } else {
18817             cfg.cn = combobox;    
18818         }
18819         
18820         
18821         var settings = this;
18822         
18823         ['xs','sm','md','lg'].map(function(size){
18824             if (settings[size]) {
18825                 cfg.cls += ' col-' + size + '-' + settings[size];
18826             }
18827         });
18828         
18829         return cfg;
18830     },
18831     
18832     initTouchView : function()
18833     {
18834         this.renderTouchView();
18835         
18836         this.touchViewEl.on('scroll', function(){
18837             this.el.dom.scrollTop = 0;
18838         }, this);
18839         
18840         this.originalValue = this.getValue();
18841         
18842         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18843         
18844         this.inputEl().on("click", this.showTouchView, this);
18845         if (this.triggerEl) {
18846             this.triggerEl.on("click", this.showTouchView, this);
18847         }
18848         
18849         
18850         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18851         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18852         
18853         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18854         
18855         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18856         this.store.on('load', this.onTouchViewLoad, this);
18857         this.store.on('loadexception', this.onTouchViewLoadException, this);
18858         
18859         if(this.hiddenName){
18860             
18861             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18862             
18863             this.hiddenField.dom.value =
18864                 this.hiddenValue !== undefined ? this.hiddenValue :
18865                 this.value !== undefined ? this.value : '';
18866         
18867             this.el.dom.removeAttribute('name');
18868             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18869         }
18870         
18871         if(this.multiple){
18872             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18873             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18874         }
18875         
18876         if(this.removable && !this.multiple){
18877             var close = this.closeTriggerEl();
18878             if(close){
18879                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18880                 close.on('click', this.removeBtnClick, this, close);
18881             }
18882         }
18883         /*
18884          * fix the bug in Safari iOS8
18885          */
18886         this.inputEl().on("focus", function(e){
18887             document.activeElement.blur();
18888         }, this);
18889         
18890         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18891         
18892         return;
18893         
18894         
18895     },
18896     
18897     renderTouchView : function()
18898     {
18899         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18900         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18901         
18902         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18903         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18904         
18905         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18906         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18907         this.touchViewBodyEl.setStyle('overflow', 'auto');
18908         
18909         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18910         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18911         
18912         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18913         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18914         
18915     },
18916     
18917     showTouchView : function()
18918     {
18919         if(this.disabled){
18920             return;
18921         }
18922         
18923         this.touchViewHeaderEl.hide();
18924
18925         if(this.modalTitle.length){
18926             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18927             this.touchViewHeaderEl.show();
18928         }
18929
18930         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18931         this.touchViewEl.show();
18932
18933         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18934         
18935         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18936         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18937
18938         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18939
18940         if(this.modalTitle.length){
18941             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18942         }
18943         
18944         this.touchViewBodyEl.setHeight(bodyHeight);
18945
18946         if(this.animate){
18947             var _this = this;
18948             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18949         }else{
18950             this.touchViewEl.addClass(['in','show']);
18951         }
18952         
18953         if(this._touchViewMask){
18954             Roo.get(document.body).addClass("x-body-masked");
18955             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18956             this._touchViewMask.setStyle('z-index', 10000);
18957             this._touchViewMask.addClass('show');
18958         }
18959         
18960         this.doTouchViewQuery();
18961         
18962     },
18963     
18964     hideTouchView : function()
18965     {
18966         this.touchViewEl.removeClass(['in','show']);
18967
18968         if(this.animate){
18969             var _this = this;
18970             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18971         }else{
18972             this.touchViewEl.setStyle('display', 'none');
18973         }
18974         
18975         if(this._touchViewMask){
18976             this._touchViewMask.removeClass('show');
18977             Roo.get(document.body).removeClass("x-body-masked");
18978         }
18979     },
18980     
18981     setTouchViewValue : function()
18982     {
18983         if(this.multiple){
18984             this.clearItem();
18985         
18986             var _this = this;
18987
18988             Roo.each(this.tickItems, function(o){
18989                 this.addItem(o);
18990             }, this);
18991         }
18992         
18993         this.hideTouchView();
18994     },
18995     
18996     doTouchViewQuery : function()
18997     {
18998         var qe = {
18999             query: '',
19000             forceAll: true,
19001             combo: this,
19002             cancel:false
19003         };
19004         
19005         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19006             return false;
19007         }
19008         
19009         if(!this.alwaysQuery || this.mode == 'local'){
19010             this.onTouchViewLoad();
19011             return;
19012         }
19013         
19014         this.store.load();
19015     },
19016     
19017     onTouchViewBeforeLoad : function(combo,opts)
19018     {
19019         return;
19020     },
19021
19022     // private
19023     onTouchViewLoad : function()
19024     {
19025         if(this.store.getCount() < 1){
19026             this.onTouchViewEmptyResults();
19027             return;
19028         }
19029         
19030         this.clearTouchView();
19031         
19032         var rawValue = this.getRawValue();
19033         
19034         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19035         
19036         this.tickItems = [];
19037         
19038         this.store.data.each(function(d, rowIndex){
19039             var row = this.touchViewListGroup.createChild(template);
19040             
19041             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19042                 row.addClass(d.data.cls);
19043             }
19044             
19045             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19046                 var cfg = {
19047                     data : d.data,
19048                     html : d.data[this.displayField]
19049                 };
19050                 
19051                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19052                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19053                 }
19054             }
19055             row.removeClass('selected');
19056             if(!this.multiple && this.valueField &&
19057                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19058             {
19059                 // radio buttons..
19060                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19061                 row.addClass('selected');
19062             }
19063             
19064             if(this.multiple && this.valueField &&
19065                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19066             {
19067                 
19068                 // checkboxes...
19069                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19070                 this.tickItems.push(d.data);
19071             }
19072             
19073             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19074             
19075         }, this);
19076         
19077         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19078         
19079         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19080
19081         if(this.modalTitle.length){
19082             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19083         }
19084
19085         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19086         
19087         if(this.mobile_restrict_height && listHeight < bodyHeight){
19088             this.touchViewBodyEl.setHeight(listHeight);
19089         }
19090         
19091         var _this = this;
19092         
19093         if(firstChecked && listHeight > bodyHeight){
19094             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19095         }
19096         
19097     },
19098     
19099     onTouchViewLoadException : function()
19100     {
19101         this.hideTouchView();
19102     },
19103     
19104     onTouchViewEmptyResults : function()
19105     {
19106         this.clearTouchView();
19107         
19108         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19109         
19110         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19111         
19112     },
19113     
19114     clearTouchView : function()
19115     {
19116         this.touchViewListGroup.dom.innerHTML = '';
19117     },
19118     
19119     onTouchViewClick : function(e, el, o)
19120     {
19121         e.preventDefault();
19122         
19123         var row = o.row;
19124         var rowIndex = o.rowIndex;
19125         
19126         var r = this.store.getAt(rowIndex);
19127         
19128         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19129             
19130             if(!this.multiple){
19131                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19132                     c.dom.removeAttribute('checked');
19133                 }, this);
19134
19135                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19136
19137                 this.setFromData(r.data);
19138
19139                 var close = this.closeTriggerEl();
19140
19141                 if(close){
19142                     close.show();
19143                 }
19144
19145                 this.hideTouchView();
19146
19147                 this.fireEvent('select', this, r, rowIndex);
19148
19149                 return;
19150             }
19151
19152             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19153                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19154                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19155                 return;
19156             }
19157
19158             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19159             this.addItem(r.data);
19160             this.tickItems.push(r.data);
19161         }
19162     },
19163     
19164     getAutoCreateNativeIOS : function()
19165     {
19166         var cfg = {
19167             cls: 'form-group' //input-group,
19168         };
19169         
19170         var combobox =  {
19171             tag: 'select',
19172             cls : 'roo-ios-select'
19173         };
19174         
19175         if (this.name) {
19176             combobox.name = this.name;
19177         }
19178         
19179         if (this.disabled) {
19180             combobox.disabled = true;
19181         }
19182         
19183         var settings = this;
19184         
19185         ['xs','sm','md','lg'].map(function(size){
19186             if (settings[size]) {
19187                 cfg.cls += ' col-' + size + '-' + settings[size];
19188             }
19189         });
19190         
19191         cfg.cn = combobox;
19192         
19193         return cfg;
19194         
19195     },
19196     
19197     initIOSView : function()
19198     {
19199         this.store.on('load', this.onIOSViewLoad, this);
19200         
19201         return;
19202     },
19203     
19204     onIOSViewLoad : function()
19205     {
19206         if(this.store.getCount() < 1){
19207             return;
19208         }
19209         
19210         this.clearIOSView();
19211         
19212         if(this.allowBlank) {
19213             
19214             var default_text = '-- SELECT --';
19215             
19216             if(this.placeholder.length){
19217                 default_text = this.placeholder;
19218             }
19219             
19220             if(this.emptyTitle.length){
19221                 default_text += ' - ' + this.emptyTitle + ' -';
19222             }
19223             
19224             var opt = this.inputEl().createChild({
19225                 tag: 'option',
19226                 value : 0,
19227                 html : default_text
19228             });
19229             
19230             var o = {};
19231             o[this.valueField] = 0;
19232             o[this.displayField] = default_text;
19233             
19234             this.ios_options.push({
19235                 data : o,
19236                 el : opt
19237             });
19238             
19239         }
19240         
19241         this.store.data.each(function(d, rowIndex){
19242             
19243             var html = '';
19244             
19245             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19246                 html = d.data[this.displayField];
19247             }
19248             
19249             var value = '';
19250             
19251             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19252                 value = d.data[this.valueField];
19253             }
19254             
19255             var option = {
19256                 tag: 'option',
19257                 value : value,
19258                 html : html
19259             };
19260             
19261             if(this.value == d.data[this.valueField]){
19262                 option['selected'] = true;
19263             }
19264             
19265             var opt = this.inputEl().createChild(option);
19266             
19267             this.ios_options.push({
19268                 data : d.data,
19269                 el : opt
19270             });
19271             
19272         }, this);
19273         
19274         this.inputEl().on('change', function(){
19275            this.fireEvent('select', this);
19276         }, this);
19277         
19278     },
19279     
19280     clearIOSView: function()
19281     {
19282         this.inputEl().dom.innerHTML = '';
19283         
19284         this.ios_options = [];
19285     },
19286     
19287     setIOSValue: function(v)
19288     {
19289         this.value = v;
19290         
19291         if(!this.ios_options){
19292             return;
19293         }
19294         
19295         Roo.each(this.ios_options, function(opts){
19296            
19297            opts.el.dom.removeAttribute('selected');
19298            
19299            if(opts.data[this.valueField] != v){
19300                return;
19301            }
19302            
19303            opts.el.dom.setAttribute('selected', true);
19304            
19305         }, this);
19306     }
19307
19308     /** 
19309     * @cfg {Boolean} grow 
19310     * @hide 
19311     */
19312     /** 
19313     * @cfg {Number} growMin 
19314     * @hide 
19315     */
19316     /** 
19317     * @cfg {Number} growMax 
19318     * @hide 
19319     */
19320     /**
19321      * @hide
19322      * @method autoSize
19323      */
19324 });
19325
19326 Roo.apply(Roo.bootstrap.ComboBox,  {
19327     
19328     header : {
19329         tag: 'div',
19330         cls: 'modal-header',
19331         cn: [
19332             {
19333                 tag: 'h4',
19334                 cls: 'modal-title'
19335             }
19336         ]
19337     },
19338     
19339     body : {
19340         tag: 'div',
19341         cls: 'modal-body',
19342         cn: [
19343             {
19344                 tag: 'ul',
19345                 cls: 'list-group'
19346             }
19347         ]
19348     },
19349     
19350     listItemRadio : {
19351         tag: 'li',
19352         cls: 'list-group-item',
19353         cn: [
19354             {
19355                 tag: 'span',
19356                 cls: 'roo-combobox-list-group-item-value'
19357             },
19358             {
19359                 tag: 'div',
19360                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19361                 cn: [
19362                     {
19363                         tag: 'input',
19364                         type: 'radio'
19365                     },
19366                     {
19367                         tag: 'label'
19368                     }
19369                 ]
19370             }
19371         ]
19372     },
19373     
19374     listItemCheckbox : {
19375         tag: 'li',
19376         cls: 'list-group-item',
19377         cn: [
19378             {
19379                 tag: 'span',
19380                 cls: 'roo-combobox-list-group-item-value'
19381             },
19382             {
19383                 tag: 'div',
19384                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19385                 cn: [
19386                     {
19387                         tag: 'input',
19388                         type: 'checkbox'
19389                     },
19390                     {
19391                         tag: 'label'
19392                     }
19393                 ]
19394             }
19395         ]
19396     },
19397     
19398     emptyResult : {
19399         tag: 'div',
19400         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19401     },
19402     
19403     footer : {
19404         tag: 'div',
19405         cls: 'modal-footer',
19406         cn: [
19407             {
19408                 tag: 'div',
19409                 cls: 'row',
19410                 cn: [
19411                     {
19412                         tag: 'div',
19413                         cls: 'col-xs-6 text-left',
19414                         cn: {
19415                             tag: 'button',
19416                             cls: 'btn btn-danger roo-touch-view-cancel',
19417                             html: 'Cancel'
19418                         }
19419                     },
19420                     {
19421                         tag: 'div',
19422                         cls: 'col-xs-6 text-right',
19423                         cn: {
19424                             tag: 'button',
19425                             cls: 'btn btn-success roo-touch-view-ok',
19426                             html: 'OK'
19427                         }
19428                     }
19429                 ]
19430             }
19431         ]
19432         
19433     }
19434 });
19435
19436 Roo.apply(Roo.bootstrap.ComboBox,  {
19437     
19438     touchViewTemplate : {
19439         tag: 'div',
19440         cls: 'modal fade roo-combobox-touch-view',
19441         cn: [
19442             {
19443                 tag: 'div',
19444                 cls: 'modal-dialog',
19445                 style : 'position:fixed', // we have to fix position....
19446                 cn: [
19447                     {
19448                         tag: 'div',
19449                         cls: 'modal-content',
19450                         cn: [
19451                             Roo.bootstrap.ComboBox.header,
19452                             Roo.bootstrap.ComboBox.body,
19453                             Roo.bootstrap.ComboBox.footer
19454                         ]
19455                     }
19456                 ]
19457             }
19458         ]
19459     }
19460 });/*
19461  * Based on:
19462  * Ext JS Library 1.1.1
19463  * Copyright(c) 2006-2007, Ext JS, LLC.
19464  *
19465  * Originally Released Under LGPL - original licence link has changed is not relivant.
19466  *
19467  * Fork - LGPL
19468  * <script type="text/javascript">
19469  */
19470
19471 /**
19472  * @class Roo.View
19473  * @extends Roo.util.Observable
19474  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19475  * This class also supports single and multi selection modes. <br>
19476  * Create a data model bound view:
19477  <pre><code>
19478  var store = new Roo.data.Store(...);
19479
19480  var view = new Roo.View({
19481     el : "my-element",
19482     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19483  
19484     singleSelect: true,
19485     selectedClass: "ydataview-selected",
19486     store: store
19487  });
19488
19489  // listen for node click?
19490  view.on("click", function(vw, index, node, e){
19491  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19492  });
19493
19494  // load XML data
19495  dataModel.load("foobar.xml");
19496  </code></pre>
19497  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19498  * <br><br>
19499  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19500  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19501  * 
19502  * Note: old style constructor is still suported (container, template, config)
19503  * 
19504  * @constructor
19505  * Create a new View
19506  * @param {Object} config The config object
19507  * 
19508  */
19509 Roo.View = function(config, depreciated_tpl, depreciated_config){
19510     
19511     this.parent = false;
19512     
19513     if (typeof(depreciated_tpl) == 'undefined') {
19514         // new way.. - universal constructor.
19515         Roo.apply(this, config);
19516         this.el  = Roo.get(this.el);
19517     } else {
19518         // old format..
19519         this.el  = Roo.get(config);
19520         this.tpl = depreciated_tpl;
19521         Roo.apply(this, depreciated_config);
19522     }
19523     this.wrapEl  = this.el.wrap().wrap();
19524     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19525     
19526     
19527     if(typeof(this.tpl) == "string"){
19528         this.tpl = new Roo.Template(this.tpl);
19529     } else {
19530         // support xtype ctors..
19531         this.tpl = new Roo.factory(this.tpl, Roo);
19532     }
19533     
19534     
19535     this.tpl.compile();
19536     
19537     /** @private */
19538     this.addEvents({
19539         /**
19540          * @event beforeclick
19541          * Fires before a click is processed. Returns false to cancel the default action.
19542          * @param {Roo.View} this
19543          * @param {Number} index The index of the target node
19544          * @param {HTMLElement} node The target node
19545          * @param {Roo.EventObject} e The raw event object
19546          */
19547             "beforeclick" : true,
19548         /**
19549          * @event click
19550          * Fires when a template node is clicked.
19551          * @param {Roo.View} this
19552          * @param {Number} index The index of the target node
19553          * @param {HTMLElement} node The target node
19554          * @param {Roo.EventObject} e The raw event object
19555          */
19556             "click" : true,
19557         /**
19558          * @event dblclick
19559          * Fires when a template node is double clicked.
19560          * @param {Roo.View} this
19561          * @param {Number} index The index of the target node
19562          * @param {HTMLElement} node The target node
19563          * @param {Roo.EventObject} e The raw event object
19564          */
19565             "dblclick" : true,
19566         /**
19567          * @event contextmenu
19568          * Fires when a template node is right clicked.
19569          * @param {Roo.View} this
19570          * @param {Number} index The index of the target node
19571          * @param {HTMLElement} node The target node
19572          * @param {Roo.EventObject} e The raw event object
19573          */
19574             "contextmenu" : true,
19575         /**
19576          * @event selectionchange
19577          * Fires when the selected nodes change.
19578          * @param {Roo.View} this
19579          * @param {Array} selections Array of the selected nodes
19580          */
19581             "selectionchange" : true,
19582     
19583         /**
19584          * @event beforeselect
19585          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19586          * @param {Roo.View} this
19587          * @param {HTMLElement} node The node to be selected
19588          * @param {Array} selections Array of currently selected nodes
19589          */
19590             "beforeselect" : true,
19591         /**
19592          * @event preparedata
19593          * Fires on every row to render, to allow you to change the data.
19594          * @param {Roo.View} this
19595          * @param {Object} data to be rendered (change this)
19596          */
19597           "preparedata" : true
19598           
19599           
19600         });
19601
19602
19603
19604     this.el.on({
19605         "click": this.onClick,
19606         "dblclick": this.onDblClick,
19607         "contextmenu": this.onContextMenu,
19608         scope:this
19609     });
19610
19611     this.selections = [];
19612     this.nodes = [];
19613     this.cmp = new Roo.CompositeElementLite([]);
19614     if(this.store){
19615         this.store = Roo.factory(this.store, Roo.data);
19616         this.setStore(this.store, true);
19617     }
19618     
19619     if ( this.footer && this.footer.xtype) {
19620            
19621          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19622         
19623         this.footer.dataSource = this.store;
19624         this.footer.container = fctr;
19625         this.footer = Roo.factory(this.footer, Roo);
19626         fctr.insertFirst(this.el);
19627         
19628         // this is a bit insane - as the paging toolbar seems to detach the el..
19629 //        dom.parentNode.parentNode.parentNode
19630          // they get detached?
19631     }
19632     
19633     
19634     Roo.View.superclass.constructor.call(this);
19635     
19636     
19637 };
19638
19639 Roo.extend(Roo.View, Roo.util.Observable, {
19640     
19641      /**
19642      * @cfg {Roo.data.Store} store Data store to load data from.
19643      */
19644     store : false,
19645     
19646     /**
19647      * @cfg {String|Roo.Element} el The container element.
19648      */
19649     el : '',
19650     
19651     /**
19652      * @cfg {String|Roo.Template} tpl The template used by this View 
19653      */
19654     tpl : false,
19655     /**
19656      * @cfg {String} dataName the named area of the template to use as the data area
19657      *                          Works with domtemplates roo-name="name"
19658      */
19659     dataName: false,
19660     /**
19661      * @cfg {String} selectedClass The css class to add to selected nodes
19662      */
19663     selectedClass : "x-view-selected",
19664      /**
19665      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19666      */
19667     emptyText : "",
19668     
19669     /**
19670      * @cfg {String} text to display on mask (default Loading)
19671      */
19672     mask : false,
19673     /**
19674      * @cfg {Boolean} multiSelect Allow multiple selection
19675      */
19676     multiSelect : false,
19677     /**
19678      * @cfg {Boolean} singleSelect Allow single selection
19679      */
19680     singleSelect:  false,
19681     
19682     /**
19683      * @cfg {Boolean} toggleSelect - selecting 
19684      */
19685     toggleSelect : false,
19686     
19687     /**
19688      * @cfg {Boolean} tickable - selecting 
19689      */
19690     tickable : false,
19691     
19692     /**
19693      * Returns the element this view is bound to.
19694      * @return {Roo.Element}
19695      */
19696     getEl : function(){
19697         return this.wrapEl;
19698     },
19699     
19700     
19701
19702     /**
19703      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19704      */
19705     refresh : function(){
19706         //Roo.log('refresh');
19707         var t = this.tpl;
19708         
19709         // if we are using something like 'domtemplate', then
19710         // the what gets used is:
19711         // t.applySubtemplate(NAME, data, wrapping data..)
19712         // the outer template then get' applied with
19713         //     the store 'extra data'
19714         // and the body get's added to the
19715         //      roo-name="data" node?
19716         //      <span class='roo-tpl-{name}'></span> ?????
19717         
19718         
19719         
19720         this.clearSelections();
19721         this.el.update("");
19722         var html = [];
19723         var records = this.store.getRange();
19724         if(records.length < 1) {
19725             
19726             // is this valid??  = should it render a template??
19727             
19728             this.el.update(this.emptyText);
19729             return;
19730         }
19731         var el = this.el;
19732         if (this.dataName) {
19733             this.el.update(t.apply(this.store.meta)); //????
19734             el = this.el.child('.roo-tpl-' + this.dataName);
19735         }
19736         
19737         for(var i = 0, len = records.length; i < len; i++){
19738             var data = this.prepareData(records[i].data, i, records[i]);
19739             this.fireEvent("preparedata", this, data, i, records[i]);
19740             
19741             var d = Roo.apply({}, data);
19742             
19743             if(this.tickable){
19744                 Roo.apply(d, {'roo-id' : Roo.id()});
19745                 
19746                 var _this = this;
19747             
19748                 Roo.each(this.parent.item, function(item){
19749                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19750                         return;
19751                     }
19752                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19753                 });
19754             }
19755             
19756             html[html.length] = Roo.util.Format.trim(
19757                 this.dataName ?
19758                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19759                     t.apply(d)
19760             );
19761         }
19762         
19763         
19764         
19765         el.update(html.join(""));
19766         this.nodes = el.dom.childNodes;
19767         this.updateIndexes(0);
19768     },
19769     
19770
19771     /**
19772      * Function to override to reformat the data that is sent to
19773      * the template for each node.
19774      * DEPRICATED - use the preparedata event handler.
19775      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19776      * a JSON object for an UpdateManager bound view).
19777      */
19778     prepareData : function(data, index, record)
19779     {
19780         this.fireEvent("preparedata", this, data, index, record);
19781         return data;
19782     },
19783
19784     onUpdate : function(ds, record){
19785         // Roo.log('on update');   
19786         this.clearSelections();
19787         var index = this.store.indexOf(record);
19788         var n = this.nodes[index];
19789         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19790         n.parentNode.removeChild(n);
19791         this.updateIndexes(index, index);
19792     },
19793
19794     
19795     
19796 // --------- FIXME     
19797     onAdd : function(ds, records, index)
19798     {
19799         //Roo.log(['on Add', ds, records, index] );        
19800         this.clearSelections();
19801         if(this.nodes.length == 0){
19802             this.refresh();
19803             return;
19804         }
19805         var n = this.nodes[index];
19806         for(var i = 0, len = records.length; i < len; i++){
19807             var d = this.prepareData(records[i].data, i, records[i]);
19808             if(n){
19809                 this.tpl.insertBefore(n, d);
19810             }else{
19811                 
19812                 this.tpl.append(this.el, d);
19813             }
19814         }
19815         this.updateIndexes(index);
19816     },
19817
19818     onRemove : function(ds, record, index){
19819        // Roo.log('onRemove');
19820         this.clearSelections();
19821         var el = this.dataName  ?
19822             this.el.child('.roo-tpl-' + this.dataName) :
19823             this.el; 
19824         
19825         el.dom.removeChild(this.nodes[index]);
19826         this.updateIndexes(index);
19827     },
19828
19829     /**
19830      * Refresh an individual node.
19831      * @param {Number} index
19832      */
19833     refreshNode : function(index){
19834         this.onUpdate(this.store, this.store.getAt(index));
19835     },
19836
19837     updateIndexes : function(startIndex, endIndex){
19838         var ns = this.nodes;
19839         startIndex = startIndex || 0;
19840         endIndex = endIndex || ns.length - 1;
19841         for(var i = startIndex; i <= endIndex; i++){
19842             ns[i].nodeIndex = i;
19843         }
19844     },
19845
19846     /**
19847      * Changes the data store this view uses and refresh the view.
19848      * @param {Store} store
19849      */
19850     setStore : function(store, initial){
19851         if(!initial && this.store){
19852             this.store.un("datachanged", this.refresh);
19853             this.store.un("add", this.onAdd);
19854             this.store.un("remove", this.onRemove);
19855             this.store.un("update", this.onUpdate);
19856             this.store.un("clear", this.refresh);
19857             this.store.un("beforeload", this.onBeforeLoad);
19858             this.store.un("load", this.onLoad);
19859             this.store.un("loadexception", this.onLoad);
19860         }
19861         if(store){
19862           
19863             store.on("datachanged", this.refresh, this);
19864             store.on("add", this.onAdd, this);
19865             store.on("remove", this.onRemove, this);
19866             store.on("update", this.onUpdate, this);
19867             store.on("clear", this.refresh, this);
19868             store.on("beforeload", this.onBeforeLoad, this);
19869             store.on("load", this.onLoad, this);
19870             store.on("loadexception", this.onLoad, this);
19871         }
19872         
19873         if(store){
19874             this.refresh();
19875         }
19876     },
19877     /**
19878      * onbeforeLoad - masks the loading area.
19879      *
19880      */
19881     onBeforeLoad : function(store,opts)
19882     {
19883          //Roo.log('onBeforeLoad');   
19884         if (!opts.add) {
19885             this.el.update("");
19886         }
19887         this.el.mask(this.mask ? this.mask : "Loading" ); 
19888     },
19889     onLoad : function ()
19890     {
19891         this.el.unmask();
19892     },
19893     
19894
19895     /**
19896      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19897      * @param {HTMLElement} node
19898      * @return {HTMLElement} The template node
19899      */
19900     findItemFromChild : function(node){
19901         var el = this.dataName  ?
19902             this.el.child('.roo-tpl-' + this.dataName,true) :
19903             this.el.dom; 
19904         
19905         if(!node || node.parentNode == el){
19906                     return node;
19907             }
19908             var p = node.parentNode;
19909             while(p && p != el){
19910             if(p.parentNode == el){
19911                 return p;
19912             }
19913             p = p.parentNode;
19914         }
19915             return null;
19916     },
19917
19918     /** @ignore */
19919     onClick : function(e){
19920         var item = this.findItemFromChild(e.getTarget());
19921         if(item){
19922             var index = this.indexOf(item);
19923             if(this.onItemClick(item, index, e) !== false){
19924                 this.fireEvent("click", this, index, item, e);
19925             }
19926         }else{
19927             this.clearSelections();
19928         }
19929     },
19930
19931     /** @ignore */
19932     onContextMenu : function(e){
19933         var item = this.findItemFromChild(e.getTarget());
19934         if(item){
19935             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19936         }
19937     },
19938
19939     /** @ignore */
19940     onDblClick : function(e){
19941         var item = this.findItemFromChild(e.getTarget());
19942         if(item){
19943             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19944         }
19945     },
19946
19947     onItemClick : function(item, index, e)
19948     {
19949         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19950             return false;
19951         }
19952         if (this.toggleSelect) {
19953             var m = this.isSelected(item) ? 'unselect' : 'select';
19954             //Roo.log(m);
19955             var _t = this;
19956             _t[m](item, true, false);
19957             return true;
19958         }
19959         if(this.multiSelect || this.singleSelect){
19960             if(this.multiSelect && e.shiftKey && this.lastSelection){
19961                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19962             }else{
19963                 this.select(item, this.multiSelect && e.ctrlKey);
19964                 this.lastSelection = item;
19965             }
19966             
19967             if(!this.tickable){
19968                 e.preventDefault();
19969             }
19970             
19971         }
19972         return true;
19973     },
19974
19975     /**
19976      * Get the number of selected nodes.
19977      * @return {Number}
19978      */
19979     getSelectionCount : function(){
19980         return this.selections.length;
19981     },
19982
19983     /**
19984      * Get the currently selected nodes.
19985      * @return {Array} An array of HTMLElements
19986      */
19987     getSelectedNodes : function(){
19988         return this.selections;
19989     },
19990
19991     /**
19992      * Get the indexes of the selected nodes.
19993      * @return {Array}
19994      */
19995     getSelectedIndexes : function(){
19996         var indexes = [], s = this.selections;
19997         for(var i = 0, len = s.length; i < len; i++){
19998             indexes.push(s[i].nodeIndex);
19999         }
20000         return indexes;
20001     },
20002
20003     /**
20004      * Clear all selections
20005      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20006      */
20007     clearSelections : function(suppressEvent){
20008         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20009             this.cmp.elements = this.selections;
20010             this.cmp.removeClass(this.selectedClass);
20011             this.selections = [];
20012             if(!suppressEvent){
20013                 this.fireEvent("selectionchange", this, this.selections);
20014             }
20015         }
20016     },
20017
20018     /**
20019      * Returns true if the passed node is selected
20020      * @param {HTMLElement/Number} node The node or node index
20021      * @return {Boolean}
20022      */
20023     isSelected : function(node){
20024         var s = this.selections;
20025         if(s.length < 1){
20026             return false;
20027         }
20028         node = this.getNode(node);
20029         return s.indexOf(node) !== -1;
20030     },
20031
20032     /**
20033      * Selects nodes.
20034      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20035      * @param {Boolean} keepExisting (optional) true to keep existing selections
20036      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20037      */
20038     select : function(nodeInfo, keepExisting, suppressEvent){
20039         if(nodeInfo instanceof Array){
20040             if(!keepExisting){
20041                 this.clearSelections(true);
20042             }
20043             for(var i = 0, len = nodeInfo.length; i < len; i++){
20044                 this.select(nodeInfo[i], true, true);
20045             }
20046             return;
20047         } 
20048         var node = this.getNode(nodeInfo);
20049         if(!node || this.isSelected(node)){
20050             return; // already selected.
20051         }
20052         if(!keepExisting){
20053             this.clearSelections(true);
20054         }
20055         
20056         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20057             Roo.fly(node).addClass(this.selectedClass);
20058             this.selections.push(node);
20059             if(!suppressEvent){
20060                 this.fireEvent("selectionchange", this, this.selections);
20061             }
20062         }
20063         
20064         
20065     },
20066       /**
20067      * Unselects nodes.
20068      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20069      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20070      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20071      */
20072     unselect : function(nodeInfo, keepExisting, suppressEvent)
20073     {
20074         if(nodeInfo instanceof Array){
20075             Roo.each(this.selections, function(s) {
20076                 this.unselect(s, nodeInfo);
20077             }, this);
20078             return;
20079         }
20080         var node = this.getNode(nodeInfo);
20081         if(!node || !this.isSelected(node)){
20082             //Roo.log("not selected");
20083             return; // not selected.
20084         }
20085         // fireevent???
20086         var ns = [];
20087         Roo.each(this.selections, function(s) {
20088             if (s == node ) {
20089                 Roo.fly(node).removeClass(this.selectedClass);
20090
20091                 return;
20092             }
20093             ns.push(s);
20094         },this);
20095         
20096         this.selections= ns;
20097         this.fireEvent("selectionchange", this, this.selections);
20098     },
20099
20100     /**
20101      * Gets a template node.
20102      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20103      * @return {HTMLElement} The node or null if it wasn't found
20104      */
20105     getNode : function(nodeInfo){
20106         if(typeof nodeInfo == "string"){
20107             return document.getElementById(nodeInfo);
20108         }else if(typeof nodeInfo == "number"){
20109             return this.nodes[nodeInfo];
20110         }
20111         return nodeInfo;
20112     },
20113
20114     /**
20115      * Gets a range template nodes.
20116      * @param {Number} startIndex
20117      * @param {Number} endIndex
20118      * @return {Array} An array of nodes
20119      */
20120     getNodes : function(start, end){
20121         var ns = this.nodes;
20122         start = start || 0;
20123         end = typeof end == "undefined" ? ns.length - 1 : end;
20124         var nodes = [];
20125         if(start <= end){
20126             for(var i = start; i <= end; i++){
20127                 nodes.push(ns[i]);
20128             }
20129         } else{
20130             for(var i = start; i >= end; i--){
20131                 nodes.push(ns[i]);
20132             }
20133         }
20134         return nodes;
20135     },
20136
20137     /**
20138      * Finds the index of the passed node
20139      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20140      * @return {Number} The index of the node or -1
20141      */
20142     indexOf : function(node){
20143         node = this.getNode(node);
20144         if(typeof node.nodeIndex == "number"){
20145             return node.nodeIndex;
20146         }
20147         var ns = this.nodes;
20148         for(var i = 0, len = ns.length; i < len; i++){
20149             if(ns[i] == node){
20150                 return i;
20151             }
20152         }
20153         return -1;
20154     }
20155 });
20156 /*
20157  * - LGPL
20158  *
20159  * based on jquery fullcalendar
20160  * 
20161  */
20162
20163 Roo.bootstrap = Roo.bootstrap || {};
20164 /**
20165  * @class Roo.bootstrap.Calendar
20166  * @extends Roo.bootstrap.Component
20167  * Bootstrap Calendar class
20168  * @cfg {Boolean} loadMask (true|false) default false
20169  * @cfg {Object} header generate the user specific header of the calendar, default false
20170
20171  * @constructor
20172  * Create a new Container
20173  * @param {Object} config The config object
20174  */
20175
20176
20177
20178 Roo.bootstrap.Calendar = function(config){
20179     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20180      this.addEvents({
20181         /**
20182              * @event select
20183              * Fires when a date is selected
20184              * @param {DatePicker} this
20185              * @param {Date} date The selected date
20186              */
20187         'select': true,
20188         /**
20189              * @event monthchange
20190              * Fires when the displayed month changes 
20191              * @param {DatePicker} this
20192              * @param {Date} date The selected month
20193              */
20194         'monthchange': true,
20195         /**
20196              * @event evententer
20197              * Fires when mouse over an event
20198              * @param {Calendar} this
20199              * @param {event} Event
20200              */
20201         'evententer': true,
20202         /**
20203              * @event eventleave
20204              * Fires when the mouse leaves an
20205              * @param {Calendar} this
20206              * @param {event}
20207              */
20208         'eventleave': true,
20209         /**
20210              * @event eventclick
20211              * Fires when the mouse click an
20212              * @param {Calendar} this
20213              * @param {event}
20214              */
20215         'eventclick': true
20216         
20217     });
20218
20219 };
20220
20221 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20222     
20223           /**
20224      * @cfg {Roo.data.Store} store
20225      * The data source for the calendar
20226      */
20227         store : false,
20228      /**
20229      * @cfg {Number} startDay
20230      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20231      */
20232     startDay : 0,
20233     
20234     loadMask : false,
20235     
20236     header : false,
20237       
20238     getAutoCreate : function(){
20239         
20240         
20241         var fc_button = function(name, corner, style, content ) {
20242             return Roo.apply({},{
20243                 tag : 'span',
20244                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20245                          (corner.length ?
20246                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20247                             ''
20248                         ),
20249                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20250                 unselectable: 'on'
20251             });
20252         };
20253         
20254         var header = {};
20255         
20256         if(!this.header){
20257             header = {
20258                 tag : 'table',
20259                 cls : 'fc-header',
20260                 style : 'width:100%',
20261                 cn : [
20262                     {
20263                         tag: 'tr',
20264                         cn : [
20265                             {
20266                                 tag : 'td',
20267                                 cls : 'fc-header-left',
20268                                 cn : [
20269                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20270                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20271                                     { tag: 'span', cls: 'fc-header-space' },
20272                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20273
20274
20275                                 ]
20276                             },
20277
20278                             {
20279                                 tag : 'td',
20280                                 cls : 'fc-header-center',
20281                                 cn : [
20282                                     {
20283                                         tag: 'span',
20284                                         cls: 'fc-header-title',
20285                                         cn : {
20286                                             tag: 'H2',
20287                                             html : 'month / year'
20288                                         }
20289                                     }
20290
20291                                 ]
20292                             },
20293                             {
20294                                 tag : 'td',
20295                                 cls : 'fc-header-right',
20296                                 cn : [
20297                               /*      fc_button('month', 'left', '', 'month' ),
20298                                     fc_button('week', '', '', 'week' ),
20299                                     fc_button('day', 'right', '', 'day' )
20300                                 */    
20301
20302                                 ]
20303                             }
20304
20305                         ]
20306                     }
20307                 ]
20308             };
20309         }
20310         
20311         header = this.header;
20312         
20313        
20314         var cal_heads = function() {
20315             var ret = [];
20316             // fixme - handle this.
20317             
20318             for (var i =0; i < Date.dayNames.length; i++) {
20319                 var d = Date.dayNames[i];
20320                 ret.push({
20321                     tag: 'th',
20322                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20323                     html : d.substring(0,3)
20324                 });
20325                 
20326             }
20327             ret[0].cls += ' fc-first';
20328             ret[6].cls += ' fc-last';
20329             return ret;
20330         };
20331         var cal_cell = function(n) {
20332             return  {
20333                 tag: 'td',
20334                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20335                 cn : [
20336                     {
20337                         cn : [
20338                             {
20339                                 cls: 'fc-day-number',
20340                                 html: 'D'
20341                             },
20342                             {
20343                                 cls: 'fc-day-content',
20344                              
20345                                 cn : [
20346                                      {
20347                                         style: 'position: relative;' // height: 17px;
20348                                     }
20349                                 ]
20350                             }
20351                             
20352                             
20353                         ]
20354                     }
20355                 ]
20356                 
20357             }
20358         };
20359         var cal_rows = function() {
20360             
20361             var ret = [];
20362             for (var r = 0; r < 6; r++) {
20363                 var row= {
20364                     tag : 'tr',
20365                     cls : 'fc-week',
20366                     cn : []
20367                 };
20368                 
20369                 for (var i =0; i < Date.dayNames.length; i++) {
20370                     var d = Date.dayNames[i];
20371                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20372
20373                 }
20374                 row.cn[0].cls+=' fc-first';
20375                 row.cn[0].cn[0].style = 'min-height:90px';
20376                 row.cn[6].cls+=' fc-last';
20377                 ret.push(row);
20378                 
20379             }
20380             ret[0].cls += ' fc-first';
20381             ret[4].cls += ' fc-prev-last';
20382             ret[5].cls += ' fc-last';
20383             return ret;
20384             
20385         };
20386         
20387         var cal_table = {
20388             tag: 'table',
20389             cls: 'fc-border-separate',
20390             style : 'width:100%',
20391             cellspacing  : 0,
20392             cn : [
20393                 { 
20394                     tag: 'thead',
20395                     cn : [
20396                         { 
20397                             tag: 'tr',
20398                             cls : 'fc-first fc-last',
20399                             cn : cal_heads()
20400                         }
20401                     ]
20402                 },
20403                 { 
20404                     tag: 'tbody',
20405                     cn : cal_rows()
20406                 }
20407                   
20408             ]
20409         };
20410          
20411          var cfg = {
20412             cls : 'fc fc-ltr',
20413             cn : [
20414                 header,
20415                 {
20416                     cls : 'fc-content',
20417                     style : "position: relative;",
20418                     cn : [
20419                         {
20420                             cls : 'fc-view fc-view-month fc-grid',
20421                             style : 'position: relative',
20422                             unselectable : 'on',
20423                             cn : [
20424                                 {
20425                                     cls : 'fc-event-container',
20426                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20427                                 },
20428                                 cal_table
20429                             ]
20430                         }
20431                     ]
20432     
20433                 }
20434            ] 
20435             
20436         };
20437         
20438          
20439         
20440         return cfg;
20441     },
20442     
20443     
20444     initEvents : function()
20445     {
20446         if(!this.store){
20447             throw "can not find store for calendar";
20448         }
20449         
20450         var mark = {
20451             tag: "div",
20452             cls:"x-dlg-mask",
20453             style: "text-align:center",
20454             cn: [
20455                 {
20456                     tag: "div",
20457                     style: "background-color:white;width:50%;margin:250 auto",
20458                     cn: [
20459                         {
20460                             tag: "img",
20461                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20462                         },
20463                         {
20464                             tag: "span",
20465                             html: "Loading"
20466                         }
20467                         
20468                     ]
20469                 }
20470             ]
20471         };
20472         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20473         
20474         var size = this.el.select('.fc-content', true).first().getSize();
20475         this.maskEl.setSize(size.width, size.height);
20476         this.maskEl.enableDisplayMode("block");
20477         if(!this.loadMask){
20478             this.maskEl.hide();
20479         }
20480         
20481         this.store = Roo.factory(this.store, Roo.data);
20482         this.store.on('load', this.onLoad, this);
20483         this.store.on('beforeload', this.onBeforeLoad, this);
20484         
20485         this.resize();
20486         
20487         this.cells = this.el.select('.fc-day',true);
20488         //Roo.log(this.cells);
20489         this.textNodes = this.el.query('.fc-day-number');
20490         this.cells.addClassOnOver('fc-state-hover');
20491         
20492         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20493         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20494         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20495         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20496         
20497         this.on('monthchange', this.onMonthChange, this);
20498         
20499         this.update(new Date().clearTime());
20500     },
20501     
20502     resize : function() {
20503         var sz  = this.el.getSize();
20504         
20505         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20506         this.el.select('.fc-day-content div',true).setHeight(34);
20507     },
20508     
20509     
20510     // private
20511     showPrevMonth : function(e){
20512         this.update(this.activeDate.add("mo", -1));
20513     },
20514     showToday : function(e){
20515         this.update(new Date().clearTime());
20516     },
20517     // private
20518     showNextMonth : function(e){
20519         this.update(this.activeDate.add("mo", 1));
20520     },
20521
20522     // private
20523     showPrevYear : function(){
20524         this.update(this.activeDate.add("y", -1));
20525     },
20526
20527     // private
20528     showNextYear : function(){
20529         this.update(this.activeDate.add("y", 1));
20530     },
20531
20532     
20533    // private
20534     update : function(date)
20535     {
20536         var vd = this.activeDate;
20537         this.activeDate = date;
20538 //        if(vd && this.el){
20539 //            var t = date.getTime();
20540 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20541 //                Roo.log('using add remove');
20542 //                
20543 //                this.fireEvent('monthchange', this, date);
20544 //                
20545 //                this.cells.removeClass("fc-state-highlight");
20546 //                this.cells.each(function(c){
20547 //                   if(c.dateValue == t){
20548 //                       c.addClass("fc-state-highlight");
20549 //                       setTimeout(function(){
20550 //                            try{c.dom.firstChild.focus();}catch(e){}
20551 //                       }, 50);
20552 //                       return false;
20553 //                   }
20554 //                   return true;
20555 //                });
20556 //                return;
20557 //            }
20558 //        }
20559         
20560         var days = date.getDaysInMonth();
20561         
20562         var firstOfMonth = date.getFirstDateOfMonth();
20563         var startingPos = firstOfMonth.getDay()-this.startDay;
20564         
20565         if(startingPos < this.startDay){
20566             startingPos += 7;
20567         }
20568         
20569         var pm = date.add(Date.MONTH, -1);
20570         var prevStart = pm.getDaysInMonth()-startingPos;
20571 //        
20572         this.cells = this.el.select('.fc-day',true);
20573         this.textNodes = this.el.query('.fc-day-number');
20574         this.cells.addClassOnOver('fc-state-hover');
20575         
20576         var cells = this.cells.elements;
20577         var textEls = this.textNodes;
20578         
20579         Roo.each(cells, function(cell){
20580             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20581         });
20582         
20583         days += startingPos;
20584
20585         // convert everything to numbers so it's fast
20586         var day = 86400000;
20587         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20588         //Roo.log(d);
20589         //Roo.log(pm);
20590         //Roo.log(prevStart);
20591         
20592         var today = new Date().clearTime().getTime();
20593         var sel = date.clearTime().getTime();
20594         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20595         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20596         var ddMatch = this.disabledDatesRE;
20597         var ddText = this.disabledDatesText;
20598         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20599         var ddaysText = this.disabledDaysText;
20600         var format = this.format;
20601         
20602         var setCellClass = function(cal, cell){
20603             cell.row = 0;
20604             cell.events = [];
20605             cell.more = [];
20606             //Roo.log('set Cell Class');
20607             cell.title = "";
20608             var t = d.getTime();
20609             
20610             //Roo.log(d);
20611             
20612             cell.dateValue = t;
20613             if(t == today){
20614                 cell.className += " fc-today";
20615                 cell.className += " fc-state-highlight";
20616                 cell.title = cal.todayText;
20617             }
20618             if(t == sel){
20619                 // disable highlight in other month..
20620                 //cell.className += " fc-state-highlight";
20621                 
20622             }
20623             // disabling
20624             if(t < min) {
20625                 cell.className = " fc-state-disabled";
20626                 cell.title = cal.minText;
20627                 return;
20628             }
20629             if(t > max) {
20630                 cell.className = " fc-state-disabled";
20631                 cell.title = cal.maxText;
20632                 return;
20633             }
20634             if(ddays){
20635                 if(ddays.indexOf(d.getDay()) != -1){
20636                     cell.title = ddaysText;
20637                     cell.className = " fc-state-disabled";
20638                 }
20639             }
20640             if(ddMatch && format){
20641                 var fvalue = d.dateFormat(format);
20642                 if(ddMatch.test(fvalue)){
20643                     cell.title = ddText.replace("%0", fvalue);
20644                     cell.className = " fc-state-disabled";
20645                 }
20646             }
20647             
20648             if (!cell.initialClassName) {
20649                 cell.initialClassName = cell.dom.className;
20650             }
20651             
20652             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20653         };
20654
20655         var i = 0;
20656         
20657         for(; i < startingPos; i++) {
20658             textEls[i].innerHTML = (++prevStart);
20659             d.setDate(d.getDate()+1);
20660             
20661             cells[i].className = "fc-past fc-other-month";
20662             setCellClass(this, cells[i]);
20663         }
20664         
20665         var intDay = 0;
20666         
20667         for(; i < days; i++){
20668             intDay = i - startingPos + 1;
20669             textEls[i].innerHTML = (intDay);
20670             d.setDate(d.getDate()+1);
20671             
20672             cells[i].className = ''; // "x-date-active";
20673             setCellClass(this, cells[i]);
20674         }
20675         var extraDays = 0;
20676         
20677         for(; i < 42; i++) {
20678             textEls[i].innerHTML = (++extraDays);
20679             d.setDate(d.getDate()+1);
20680             
20681             cells[i].className = "fc-future fc-other-month";
20682             setCellClass(this, cells[i]);
20683         }
20684         
20685         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20686         
20687         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20688         
20689         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20690         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20691         
20692         if(totalRows != 6){
20693             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20694             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20695         }
20696         
20697         this.fireEvent('monthchange', this, date);
20698         
20699         
20700         /*
20701         if(!this.internalRender){
20702             var main = this.el.dom.firstChild;
20703             var w = main.offsetWidth;
20704             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20705             Roo.fly(main).setWidth(w);
20706             this.internalRender = true;
20707             // opera does not respect the auto grow header center column
20708             // then, after it gets a width opera refuses to recalculate
20709             // without a second pass
20710             if(Roo.isOpera && !this.secondPass){
20711                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20712                 this.secondPass = true;
20713                 this.update.defer(10, this, [date]);
20714             }
20715         }
20716         */
20717         
20718     },
20719     
20720     findCell : function(dt) {
20721         dt = dt.clearTime().getTime();
20722         var ret = false;
20723         this.cells.each(function(c){
20724             //Roo.log("check " +c.dateValue + '?=' + dt);
20725             if(c.dateValue == dt){
20726                 ret = c;
20727                 return false;
20728             }
20729             return true;
20730         });
20731         
20732         return ret;
20733     },
20734     
20735     findCells : function(ev) {
20736         var s = ev.start.clone().clearTime().getTime();
20737        // Roo.log(s);
20738         var e= ev.end.clone().clearTime().getTime();
20739        // Roo.log(e);
20740         var ret = [];
20741         this.cells.each(function(c){
20742              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20743             
20744             if(c.dateValue > e){
20745                 return ;
20746             }
20747             if(c.dateValue < s){
20748                 return ;
20749             }
20750             ret.push(c);
20751         });
20752         
20753         return ret;    
20754     },
20755     
20756 //    findBestRow: function(cells)
20757 //    {
20758 //        var ret = 0;
20759 //        
20760 //        for (var i =0 ; i < cells.length;i++) {
20761 //            ret  = Math.max(cells[i].rows || 0,ret);
20762 //        }
20763 //        return ret;
20764 //        
20765 //    },
20766     
20767     
20768     addItem : function(ev)
20769     {
20770         // look for vertical location slot in
20771         var cells = this.findCells(ev);
20772         
20773 //        ev.row = this.findBestRow(cells);
20774         
20775         // work out the location.
20776         
20777         var crow = false;
20778         var rows = [];
20779         for(var i =0; i < cells.length; i++) {
20780             
20781             cells[i].row = cells[0].row;
20782             
20783             if(i == 0){
20784                 cells[i].row = cells[i].row + 1;
20785             }
20786             
20787             if (!crow) {
20788                 crow = {
20789                     start : cells[i],
20790                     end :  cells[i]
20791                 };
20792                 continue;
20793             }
20794             if (crow.start.getY() == cells[i].getY()) {
20795                 // on same row.
20796                 crow.end = cells[i];
20797                 continue;
20798             }
20799             // different row.
20800             rows.push(crow);
20801             crow = {
20802                 start: cells[i],
20803                 end : cells[i]
20804             };
20805             
20806         }
20807         
20808         rows.push(crow);
20809         ev.els = [];
20810         ev.rows = rows;
20811         ev.cells = cells;
20812         
20813         cells[0].events.push(ev);
20814         
20815         this.calevents.push(ev);
20816     },
20817     
20818     clearEvents: function() {
20819         
20820         if(!this.calevents){
20821             return;
20822         }
20823         
20824         Roo.each(this.cells.elements, function(c){
20825             c.row = 0;
20826             c.events = [];
20827             c.more = [];
20828         });
20829         
20830         Roo.each(this.calevents, function(e) {
20831             Roo.each(e.els, function(el) {
20832                 el.un('mouseenter' ,this.onEventEnter, this);
20833                 el.un('mouseleave' ,this.onEventLeave, this);
20834                 el.remove();
20835             },this);
20836         },this);
20837         
20838         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20839             e.remove();
20840         });
20841         
20842     },
20843     
20844     renderEvents: function()
20845     {   
20846         var _this = this;
20847         
20848         this.cells.each(function(c) {
20849             
20850             if(c.row < 5){
20851                 return;
20852             }
20853             
20854             var ev = c.events;
20855             
20856             var r = 4;
20857             if(c.row != c.events.length){
20858                 r = 4 - (4 - (c.row - c.events.length));
20859             }
20860             
20861             c.events = ev.slice(0, r);
20862             c.more = ev.slice(r);
20863             
20864             if(c.more.length && c.more.length == 1){
20865                 c.events.push(c.more.pop());
20866             }
20867             
20868             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20869             
20870         });
20871             
20872         this.cells.each(function(c) {
20873             
20874             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20875             
20876             
20877             for (var e = 0; e < c.events.length; e++){
20878                 var ev = c.events[e];
20879                 var rows = ev.rows;
20880                 
20881                 for(var i = 0; i < rows.length; i++) {
20882                 
20883                     // how many rows should it span..
20884
20885                     var  cfg = {
20886                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20887                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20888
20889                         unselectable : "on",
20890                         cn : [
20891                             {
20892                                 cls: 'fc-event-inner',
20893                                 cn : [
20894     //                                {
20895     //                                  tag:'span',
20896     //                                  cls: 'fc-event-time',
20897     //                                  html : cells.length > 1 ? '' : ev.time
20898     //                                },
20899                                     {
20900                                       tag:'span',
20901                                       cls: 'fc-event-title',
20902                                       html : String.format('{0}', ev.title)
20903                                     }
20904
20905
20906                                 ]
20907                             },
20908                             {
20909                                 cls: 'ui-resizable-handle ui-resizable-e',
20910                                 html : '&nbsp;&nbsp;&nbsp'
20911                             }
20912
20913                         ]
20914                     };
20915
20916                     if (i == 0) {
20917                         cfg.cls += ' fc-event-start';
20918                     }
20919                     if ((i+1) == rows.length) {
20920                         cfg.cls += ' fc-event-end';
20921                     }
20922
20923                     var ctr = _this.el.select('.fc-event-container',true).first();
20924                     var cg = ctr.createChild(cfg);
20925
20926                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20927                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20928
20929                     var r = (c.more.length) ? 1 : 0;
20930                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20931                     cg.setWidth(ebox.right - sbox.x -2);
20932
20933                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20934                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20935                     cg.on('click', _this.onEventClick, _this, ev);
20936
20937                     ev.els.push(cg);
20938                     
20939                 }
20940                 
20941             }
20942             
20943             
20944             if(c.more.length){
20945                 var  cfg = {
20946                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20947                     style : 'position: absolute',
20948                     unselectable : "on",
20949                     cn : [
20950                         {
20951                             cls: 'fc-event-inner',
20952                             cn : [
20953                                 {
20954                                   tag:'span',
20955                                   cls: 'fc-event-title',
20956                                   html : 'More'
20957                                 }
20958
20959
20960                             ]
20961                         },
20962                         {
20963                             cls: 'ui-resizable-handle ui-resizable-e',
20964                             html : '&nbsp;&nbsp;&nbsp'
20965                         }
20966
20967                     ]
20968                 };
20969
20970                 var ctr = _this.el.select('.fc-event-container',true).first();
20971                 var cg = ctr.createChild(cfg);
20972
20973                 var sbox = c.select('.fc-day-content',true).first().getBox();
20974                 var ebox = c.select('.fc-day-content',true).first().getBox();
20975                 //Roo.log(cg);
20976                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20977                 cg.setWidth(ebox.right - sbox.x -2);
20978
20979                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20980                 
20981             }
20982             
20983         });
20984         
20985         
20986         
20987     },
20988     
20989     onEventEnter: function (e, el,event,d) {
20990         this.fireEvent('evententer', this, el, event);
20991     },
20992     
20993     onEventLeave: function (e, el,event,d) {
20994         this.fireEvent('eventleave', this, el, event);
20995     },
20996     
20997     onEventClick: function (e, el,event,d) {
20998         this.fireEvent('eventclick', this, el, event);
20999     },
21000     
21001     onMonthChange: function () {
21002         this.store.load();
21003     },
21004     
21005     onMoreEventClick: function(e, el, more)
21006     {
21007         var _this = this;
21008         
21009         this.calpopover.placement = 'right';
21010         this.calpopover.setTitle('More');
21011         
21012         this.calpopover.setContent('');
21013         
21014         var ctr = this.calpopover.el.select('.popover-content', true).first();
21015         
21016         Roo.each(more, function(m){
21017             var cfg = {
21018                 cls : 'fc-event-hori fc-event-draggable',
21019                 html : m.title
21020             };
21021             var cg = ctr.createChild(cfg);
21022             
21023             cg.on('click', _this.onEventClick, _this, m);
21024         });
21025         
21026         this.calpopover.show(el);
21027         
21028         
21029     },
21030     
21031     onLoad: function () 
21032     {   
21033         this.calevents = [];
21034         var cal = this;
21035         
21036         if(this.store.getCount() > 0){
21037             this.store.data.each(function(d){
21038                cal.addItem({
21039                     id : d.data.id,
21040                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21041                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21042                     time : d.data.start_time,
21043                     title : d.data.title,
21044                     description : d.data.description,
21045                     venue : d.data.venue
21046                 });
21047             });
21048         }
21049         
21050         this.renderEvents();
21051         
21052         if(this.calevents.length && this.loadMask){
21053             this.maskEl.hide();
21054         }
21055     },
21056     
21057     onBeforeLoad: function()
21058     {
21059         this.clearEvents();
21060         if(this.loadMask){
21061             this.maskEl.show();
21062         }
21063     }
21064 });
21065
21066  
21067  /*
21068  * - LGPL
21069  *
21070  * element
21071  * 
21072  */
21073
21074 /**
21075  * @class Roo.bootstrap.Popover
21076  * @extends Roo.bootstrap.Component
21077  * @builder-top
21078  * Bootstrap Popover class
21079  * @cfg {String} html contents of the popover   (or false to use children..)
21080  * @cfg {String} title of popover (or false to hide)
21081  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21082  * @cfg {String} trigger click || hover (or false to trigger manually)
21083  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21084  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21085  *      - if false and it has a 'parent' then it will be automatically added to that element
21086  *      - if string - Roo.get  will be called 
21087  * @cfg {Number} delay - delay before showing
21088  
21089  * @constructor
21090  * Create a new Popover
21091  * @param {Object} config The config object
21092  */
21093
21094 Roo.bootstrap.Popover = function(config){
21095     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21096     
21097     this.addEvents({
21098         // raw events
21099          /**
21100          * @event show
21101          * After the popover show
21102          * 
21103          * @param {Roo.bootstrap.Popover} this
21104          */
21105         "show" : true,
21106         /**
21107          * @event hide
21108          * After the popover hide
21109          * 
21110          * @param {Roo.bootstrap.Popover} this
21111          */
21112         "hide" : true
21113     });
21114 };
21115
21116 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21117     
21118     title: false,
21119     html: false,
21120     
21121     placement : 'right',
21122     trigger : 'hover', // hover
21123     modal : false,
21124     delay : 0,
21125     
21126     over: false,
21127     
21128     can_build_overlaid : false,
21129     
21130     maskEl : false, // the mask element
21131     headerEl : false,
21132     contentEl : false,
21133     alignEl : false, // when show is called with an element - this get's stored.
21134     
21135     getChildContainer : function()
21136     {
21137         return this.contentEl;
21138         
21139     },
21140     getPopoverHeader : function()
21141     {
21142         this.title = true; // flag not to hide it..
21143         this.headerEl.addClass('p-0');
21144         return this.headerEl
21145     },
21146     
21147     
21148     getAutoCreate : function(){
21149          
21150         var cfg = {
21151            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21152            style: 'display:block',
21153            cn : [
21154                 {
21155                     cls : 'arrow'
21156                 },
21157                 {
21158                     cls : 'popover-inner ',
21159                     cn : [
21160                         {
21161                             tag: 'h3',
21162                             cls: 'popover-title popover-header',
21163                             html : this.title === false ? '' : this.title
21164                         },
21165                         {
21166                             cls : 'popover-content popover-body '  + (this.cls || ''),
21167                             html : this.html || ''
21168                         }
21169                     ]
21170                     
21171                 }
21172            ]
21173         };
21174         
21175         return cfg;
21176     },
21177     /**
21178      * @param {string} the title
21179      */
21180     setTitle: function(str)
21181     {
21182         this.title = str;
21183         if (this.el) {
21184             this.headerEl.dom.innerHTML = str;
21185         }
21186         
21187     },
21188     /**
21189      * @param {string} the body content
21190      */
21191     setContent: function(str)
21192     {
21193         this.html = str;
21194         if (this.contentEl) {
21195             this.contentEl.dom.innerHTML = str;
21196         }
21197         
21198     },
21199     // as it get's added to the bottom of the page.
21200     onRender : function(ct, position)
21201     {
21202         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21203         
21204         
21205         
21206         if(!this.el){
21207             var cfg = Roo.apply({},  this.getAutoCreate());
21208             cfg.id = Roo.id();
21209             
21210             if (this.cls) {
21211                 cfg.cls += ' ' + this.cls;
21212             }
21213             if (this.style) {
21214                 cfg.style = this.style;
21215             }
21216             //Roo.log("adding to ");
21217             this.el = Roo.get(document.body).createChild(cfg, position);
21218 //            Roo.log(this.el);
21219         }
21220         
21221         this.contentEl = this.el.select('.popover-content',true).first();
21222         this.headerEl =  this.el.select('.popover-title',true).first();
21223         
21224         var nitems = [];
21225         if(typeof(this.items) != 'undefined'){
21226             var items = this.items;
21227             delete this.items;
21228
21229             for(var i =0;i < items.length;i++) {
21230                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21231             }
21232         }
21233
21234         this.items = nitems;
21235         
21236         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21237         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21238         
21239         
21240         
21241         this.initEvents();
21242     },
21243     
21244     resizeMask : function()
21245     {
21246         this.maskEl.setSize(
21247             Roo.lib.Dom.getViewWidth(true),
21248             Roo.lib.Dom.getViewHeight(true)
21249         );
21250     },
21251     
21252     initEvents : function()
21253     {
21254         
21255         if (!this.modal) { 
21256             Roo.bootstrap.Popover.register(this);
21257         }
21258          
21259         this.arrowEl = this.el.select('.arrow',true).first();
21260         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21261         this.el.enableDisplayMode('block');
21262         this.el.hide();
21263  
21264         
21265         if (this.over === false && !this.parent()) {
21266             return; 
21267         }
21268         if (this.triggers === false) {
21269             return;
21270         }
21271          
21272         // support parent
21273         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21274         var triggers = this.trigger ? this.trigger.split(' ') : [];
21275         Roo.each(triggers, function(trigger) {
21276         
21277             if (trigger == 'click') {
21278                 on_el.on('click', this.toggle, this);
21279             } else if (trigger != 'manual') {
21280                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21281                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21282       
21283                 on_el.on(eventIn  ,this.enter, this);
21284                 on_el.on(eventOut, this.leave, this);
21285             }
21286         }, this);
21287     },
21288     
21289     
21290     // private
21291     timeout : null,
21292     hoverState : null,
21293     
21294     toggle : function () {
21295         this.hoverState == 'in' ? this.leave() : this.enter();
21296     },
21297     
21298     enter : function () {
21299         
21300         clearTimeout(this.timeout);
21301     
21302         this.hoverState = 'in';
21303     
21304         if (!this.delay || !this.delay.show) {
21305             this.show();
21306             return;
21307         }
21308         var _t = this;
21309         this.timeout = setTimeout(function () {
21310             if (_t.hoverState == 'in') {
21311                 _t.show();
21312             }
21313         }, this.delay.show)
21314     },
21315     
21316     leave : function() {
21317         clearTimeout(this.timeout);
21318     
21319         this.hoverState = 'out';
21320     
21321         if (!this.delay || !this.delay.hide) {
21322             this.hide();
21323             return;
21324         }
21325         var _t = this;
21326         this.timeout = setTimeout(function () {
21327             if (_t.hoverState == 'out') {
21328                 _t.hide();
21329             }
21330         }, this.delay.hide)
21331     },
21332     /**
21333      * Show the popover
21334      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21335      * @param {string} (left|right|top|bottom) position
21336      */
21337     show : function (on_el, placement)
21338     {
21339         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21340         on_el = on_el || false; // default to false
21341          
21342         if (!on_el) {
21343             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21344                 on_el = this.parent().el;
21345             } else if (this.over) {
21346                 on_el = Roo.get(this.over);
21347             }
21348             
21349         }
21350         
21351         this.alignEl = Roo.get( on_el );
21352
21353         if (!this.el) {
21354             this.render(document.body);
21355         }
21356         
21357         
21358          
21359         
21360         if (this.title === false) {
21361             this.headerEl.hide();
21362         }
21363         
21364        
21365         this.el.show();
21366         this.el.dom.style.display = 'block';
21367          
21368  
21369         if (this.alignEl) {
21370             this.updatePosition(this.placement, true);
21371              
21372         } else {
21373             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21374             var es = this.el.getSize();
21375             var x = Roo.lib.Dom.getViewWidth()/2;
21376             var y = Roo.lib.Dom.getViewHeight()/2;
21377             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21378             
21379         }
21380
21381         
21382         //var arrow = this.el.select('.arrow',true).first();
21383         //arrow.set(align[2], 
21384         
21385         this.el.addClass('in');
21386         
21387          
21388         
21389         this.hoverState = 'in';
21390         
21391         if (this.modal) {
21392             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21393             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21394             this.maskEl.dom.style.display = 'block';
21395             this.maskEl.addClass('show');
21396         }
21397         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21398  
21399         this.fireEvent('show', this);
21400         
21401     },
21402     /**
21403      * fire this manually after loading a grid in the table for example
21404      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21405      * @param {Boolean} try and move it if we cant get right position.
21406      */
21407     updatePosition : function(placement, try_move)
21408     {
21409         // allow for calling with no parameters
21410         placement = placement   ? placement :  this.placement;
21411         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21412         
21413         this.el.removeClass([
21414             'fade','top','bottom', 'left', 'right','in',
21415             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21416         ]);
21417         this.el.addClass(placement + ' bs-popover-' + placement);
21418         
21419         if (!this.alignEl ) {
21420             return false;
21421         }
21422         
21423         switch (placement) {
21424             case 'right':
21425                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21426                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21427                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21428                     //normal display... or moved up/down.
21429                     this.el.setXY(offset);
21430                     var xy = this.alignEl.getAnchorXY('tr', false);
21431                     xy[0]+=2;xy[1]+=5;
21432                     this.arrowEl.setXY(xy);
21433                     return true;
21434                 }
21435                 // continue through...
21436                 return this.updatePosition('left', false);
21437                 
21438             
21439             case 'left':
21440                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21441                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21442                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21443                     //normal display... or moved up/down.
21444                     this.el.setXY(offset);
21445                     var xy = this.alignEl.getAnchorXY('tl', false);
21446                     xy[0]-=10;xy[1]+=5; // << fix me
21447                     this.arrowEl.setXY(xy);
21448                     return true;
21449                 }
21450                 // call self...
21451                 return this.updatePosition('right', false);
21452             
21453             case 'top':
21454                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21455                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21456                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21457                     //normal display... or moved up/down.
21458                     this.el.setXY(offset);
21459                     var xy = this.alignEl.getAnchorXY('t', false);
21460                     xy[1]-=10; // << fix me
21461                     this.arrowEl.setXY(xy);
21462                     return true;
21463                 }
21464                 // fall through
21465                return this.updatePosition('bottom', false);
21466             
21467             case 'bottom':
21468                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21469                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21470                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21471                     //normal display... or moved up/down.
21472                     this.el.setXY(offset);
21473                     var xy = this.alignEl.getAnchorXY('b', false);
21474                      xy[1]+=2; // << fix me
21475                     this.arrowEl.setXY(xy);
21476                     return true;
21477                 }
21478                 // fall through
21479                 return this.updatePosition('top', false);
21480                 
21481             
21482         }
21483         
21484         
21485         return false;
21486     },
21487     
21488     hide : function()
21489     {
21490         this.el.setXY([0,0]);
21491         this.el.removeClass('in');
21492         this.el.hide();
21493         this.hoverState = null;
21494         this.maskEl.hide(); // always..
21495         this.fireEvent('hide', this);
21496     }
21497     
21498 });
21499
21500
21501 Roo.apply(Roo.bootstrap.Popover, {
21502
21503     alignment : {
21504         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21505         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21506         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21507         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21508     },
21509     
21510     zIndex : 20001,
21511
21512     clickHander : false,
21513     
21514     
21515
21516     onMouseDown : function(e)
21517     {
21518         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21519             /// what is nothing is showing..
21520             this.hideAll();
21521         }
21522          
21523     },
21524     
21525     
21526     popups : [],
21527     
21528     register : function(popup)
21529     {
21530         if (!Roo.bootstrap.Popover.clickHandler) {
21531             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21532         }
21533         // hide other popups.
21534         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21535         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21536         this.hideAll(); //<< why?
21537         //this.popups.push(popup);
21538     },
21539     hideAll : function()
21540     {
21541         this.popups.forEach(function(p) {
21542             p.hide();
21543         });
21544     },
21545     onShow : function() {
21546         Roo.bootstrap.Popover.popups.push(this);
21547     },
21548     onHide : function() {
21549         Roo.bootstrap.Popover.popups.remove(this);
21550     } 
21551
21552 });/*
21553  * - LGPL
21554  *
21555  * Card header - holder for the card header elements.
21556  * 
21557  */
21558
21559 /**
21560  * @class Roo.bootstrap.PopoverNav
21561  * @extends Roo.bootstrap.NavGroup
21562  * Bootstrap Popover header navigation class
21563  * @constructor
21564  * Create a new Popover Header Navigation 
21565  * @param {Object} config The config object
21566  */
21567
21568 Roo.bootstrap.PopoverNav = function(config){
21569     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21570 };
21571
21572 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21573     
21574     
21575     container_method : 'getPopoverHeader' 
21576     
21577      
21578     
21579     
21580    
21581 });
21582
21583  
21584
21585  /*
21586  * - LGPL
21587  *
21588  * Progress
21589  * 
21590  */
21591
21592 /**
21593  * @class Roo.bootstrap.Progress
21594  * @extends Roo.bootstrap.Component
21595  * Bootstrap Progress class
21596  * @cfg {Boolean} striped striped of the progress bar
21597  * @cfg {Boolean} active animated of the progress bar
21598  * 
21599  * 
21600  * @constructor
21601  * Create a new Progress
21602  * @param {Object} config The config object
21603  */
21604
21605 Roo.bootstrap.Progress = function(config){
21606     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21607 };
21608
21609 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21610     
21611     striped : false,
21612     active: false,
21613     
21614     getAutoCreate : function(){
21615         var cfg = {
21616             tag: 'div',
21617             cls: 'progress'
21618         };
21619         
21620         
21621         if(this.striped){
21622             cfg.cls += ' progress-striped';
21623         }
21624       
21625         if(this.active){
21626             cfg.cls += ' active';
21627         }
21628         
21629         
21630         return cfg;
21631     }
21632    
21633 });
21634
21635  
21636
21637  /*
21638  * - LGPL
21639  *
21640  * ProgressBar
21641  * 
21642  */
21643
21644 /**
21645  * @class Roo.bootstrap.ProgressBar
21646  * @extends Roo.bootstrap.Component
21647  * Bootstrap ProgressBar class
21648  * @cfg {Number} aria_valuenow aria-value now
21649  * @cfg {Number} aria_valuemin aria-value min
21650  * @cfg {Number} aria_valuemax aria-value max
21651  * @cfg {String} label label for the progress bar
21652  * @cfg {String} panel (success | info | warning | danger )
21653  * @cfg {String} role role of the progress bar
21654  * @cfg {String} sr_only text
21655  * 
21656  * 
21657  * @constructor
21658  * Create a new ProgressBar
21659  * @param {Object} config The config object
21660  */
21661
21662 Roo.bootstrap.ProgressBar = function(config){
21663     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21664 };
21665
21666 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21667     
21668     aria_valuenow : 0,
21669     aria_valuemin : 0,
21670     aria_valuemax : 100,
21671     label : false,
21672     panel : false,
21673     role : false,
21674     sr_only: false,
21675     
21676     getAutoCreate : function()
21677     {
21678         
21679         var cfg = {
21680             tag: 'div',
21681             cls: 'progress-bar',
21682             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21683         };
21684         
21685         if(this.sr_only){
21686             cfg.cn = {
21687                 tag: 'span',
21688                 cls: 'sr-only',
21689                 html: this.sr_only
21690             }
21691         }
21692         
21693         if(this.role){
21694             cfg.role = this.role;
21695         }
21696         
21697         if(this.aria_valuenow){
21698             cfg['aria-valuenow'] = this.aria_valuenow;
21699         }
21700         
21701         if(this.aria_valuemin){
21702             cfg['aria-valuemin'] = this.aria_valuemin;
21703         }
21704         
21705         if(this.aria_valuemax){
21706             cfg['aria-valuemax'] = this.aria_valuemax;
21707         }
21708         
21709         if(this.label && !this.sr_only){
21710             cfg.html = this.label;
21711         }
21712         
21713         if(this.panel){
21714             cfg.cls += ' progress-bar-' + this.panel;
21715         }
21716         
21717         return cfg;
21718     },
21719     
21720     update : function(aria_valuenow)
21721     {
21722         this.aria_valuenow = aria_valuenow;
21723         
21724         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21725     }
21726    
21727 });
21728
21729  
21730
21731  /*
21732  * - LGPL
21733  *
21734  * column
21735  * 
21736  */
21737
21738 /**
21739  * @class Roo.bootstrap.TabGroup
21740  * @extends Roo.bootstrap.Column
21741  * Bootstrap Column class
21742  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21743  * @cfg {Boolean} carousel true to make the group behave like a carousel
21744  * @cfg {Boolean} bullets show bullets for the panels
21745  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21746  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21747  * @cfg {Boolean} showarrow (true|false) show arrow default true
21748  * 
21749  * @constructor
21750  * Create a new TabGroup
21751  * @param {Object} config The config object
21752  */
21753
21754 Roo.bootstrap.TabGroup = function(config){
21755     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21756     if (!this.navId) {
21757         this.navId = Roo.id();
21758     }
21759     this.tabs = [];
21760     Roo.bootstrap.TabGroup.register(this);
21761     
21762 };
21763
21764 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21765     
21766     carousel : false,
21767     transition : false,
21768     bullets : 0,
21769     timer : 0,
21770     autoslide : false,
21771     slideFn : false,
21772     slideOnTouch : false,
21773     showarrow : true,
21774     
21775     getAutoCreate : function()
21776     {
21777         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21778         
21779         cfg.cls += ' tab-content';
21780         
21781         if (this.carousel) {
21782             cfg.cls += ' carousel slide';
21783             
21784             cfg.cn = [{
21785                cls : 'carousel-inner',
21786                cn : []
21787             }];
21788         
21789             if(this.bullets  && !Roo.isTouch){
21790                 
21791                 var bullets = {
21792                     cls : 'carousel-bullets',
21793                     cn : []
21794                 };
21795                
21796                 if(this.bullets_cls){
21797                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21798                 }
21799                 
21800                 bullets.cn.push({
21801                     cls : 'clear'
21802                 });
21803                 
21804                 cfg.cn[0].cn.push(bullets);
21805             }
21806             
21807             if(this.showarrow){
21808                 cfg.cn[0].cn.push({
21809                     tag : 'div',
21810                     class : 'carousel-arrow',
21811                     cn : [
21812                         {
21813                             tag : 'div',
21814                             class : 'carousel-prev',
21815                             cn : [
21816                                 {
21817                                     tag : 'i',
21818                                     class : 'fa fa-chevron-left'
21819                                 }
21820                             ]
21821                         },
21822                         {
21823                             tag : 'div',
21824                             class : 'carousel-next',
21825                             cn : [
21826                                 {
21827                                     tag : 'i',
21828                                     class : 'fa fa-chevron-right'
21829                                 }
21830                             ]
21831                         }
21832                     ]
21833                 });
21834             }
21835             
21836         }
21837         
21838         return cfg;
21839     },
21840     
21841     initEvents:  function()
21842     {
21843 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21844 //            this.el.on("touchstart", this.onTouchStart, this);
21845 //        }
21846         
21847         if(this.autoslide){
21848             var _this = this;
21849             
21850             this.slideFn = window.setInterval(function() {
21851                 _this.showPanelNext();
21852             }, this.timer);
21853         }
21854         
21855         if(this.showarrow){
21856             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21857             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21858         }
21859         
21860         
21861     },
21862     
21863 //    onTouchStart : function(e, el, o)
21864 //    {
21865 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21866 //            return;
21867 //        }
21868 //        
21869 //        this.showPanelNext();
21870 //    },
21871     
21872     
21873     getChildContainer : function()
21874     {
21875         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21876     },
21877     
21878     /**
21879     * register a Navigation item
21880     * @param {Roo.bootstrap.NavItem} the navitem to add
21881     */
21882     register : function(item)
21883     {
21884         this.tabs.push( item);
21885         item.navId = this.navId; // not really needed..
21886         this.addBullet();
21887     
21888     },
21889     
21890     getActivePanel : function()
21891     {
21892         var r = false;
21893         Roo.each(this.tabs, function(t) {
21894             if (t.active) {
21895                 r = t;
21896                 return false;
21897             }
21898             return null;
21899         });
21900         return r;
21901         
21902     },
21903     getPanelByName : function(n)
21904     {
21905         var r = false;
21906         Roo.each(this.tabs, function(t) {
21907             if (t.tabId == n) {
21908                 r = t;
21909                 return false;
21910             }
21911             return null;
21912         });
21913         return r;
21914     },
21915     indexOfPanel : function(p)
21916     {
21917         var r = false;
21918         Roo.each(this.tabs, function(t,i) {
21919             if (t.tabId == p.tabId) {
21920                 r = i;
21921                 return false;
21922             }
21923             return null;
21924         });
21925         return r;
21926     },
21927     /**
21928      * show a specific panel
21929      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21930      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21931      */
21932     showPanel : function (pan)
21933     {
21934         if(this.transition || typeof(pan) == 'undefined'){
21935             Roo.log("waiting for the transitionend");
21936             return false;
21937         }
21938         
21939         if (typeof(pan) == 'number') {
21940             pan = this.tabs[pan];
21941         }
21942         
21943         if (typeof(pan) == 'string') {
21944             pan = this.getPanelByName(pan);
21945         }
21946         
21947         var cur = this.getActivePanel();
21948         
21949         if(!pan || !cur){
21950             Roo.log('pan or acitve pan is undefined');
21951             return false;
21952         }
21953         
21954         if (pan.tabId == this.getActivePanel().tabId) {
21955             return true;
21956         }
21957         
21958         if (false === cur.fireEvent('beforedeactivate')) {
21959             return false;
21960         }
21961         
21962         if(this.bullets > 0 && !Roo.isTouch){
21963             this.setActiveBullet(this.indexOfPanel(pan));
21964         }
21965         
21966         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21967             
21968             //class="carousel-item carousel-item-next carousel-item-left"
21969             
21970             this.transition = true;
21971             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21972             var lr = dir == 'next' ? 'left' : 'right';
21973             pan.el.addClass(dir); // or prev
21974             pan.el.addClass('carousel-item-' + dir); // or prev
21975             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21976             cur.el.addClass(lr); // or right
21977             pan.el.addClass(lr);
21978             cur.el.addClass('carousel-item-' +lr); // or right
21979             pan.el.addClass('carousel-item-' +lr);
21980             
21981             
21982             var _this = this;
21983             cur.el.on('transitionend', function() {
21984                 Roo.log("trans end?");
21985                 
21986                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21987                 pan.setActive(true);
21988                 
21989                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21990                 cur.setActive(false);
21991                 
21992                 _this.transition = false;
21993                 
21994             }, this, { single:  true } );
21995             
21996             return true;
21997         }
21998         
21999         cur.setActive(false);
22000         pan.setActive(true);
22001         
22002         return true;
22003         
22004     },
22005     showPanelNext : function()
22006     {
22007         var i = this.indexOfPanel(this.getActivePanel());
22008         
22009         if (i >= this.tabs.length - 1 && !this.autoslide) {
22010             return;
22011         }
22012         
22013         if (i >= this.tabs.length - 1 && this.autoslide) {
22014             i = -1;
22015         }
22016         
22017         this.showPanel(this.tabs[i+1]);
22018     },
22019     
22020     showPanelPrev : function()
22021     {
22022         var i = this.indexOfPanel(this.getActivePanel());
22023         
22024         if (i  < 1 && !this.autoslide) {
22025             return;
22026         }
22027         
22028         if (i < 1 && this.autoslide) {
22029             i = this.tabs.length;
22030         }
22031         
22032         this.showPanel(this.tabs[i-1]);
22033     },
22034     
22035     
22036     addBullet: function()
22037     {
22038         if(!this.bullets || Roo.isTouch){
22039             return;
22040         }
22041         var ctr = this.el.select('.carousel-bullets',true).first();
22042         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22043         var bullet = ctr.createChild({
22044             cls : 'bullet bullet-' + i
22045         },ctr.dom.lastChild);
22046         
22047         
22048         var _this = this;
22049         
22050         bullet.on('click', (function(e, el, o, ii, t){
22051
22052             e.preventDefault();
22053
22054             this.showPanel(ii);
22055
22056             if(this.autoslide && this.slideFn){
22057                 clearInterval(this.slideFn);
22058                 this.slideFn = window.setInterval(function() {
22059                     _this.showPanelNext();
22060                 }, this.timer);
22061             }
22062
22063         }).createDelegate(this, [i, bullet], true));
22064                 
22065         
22066     },
22067      
22068     setActiveBullet : function(i)
22069     {
22070         if(Roo.isTouch){
22071             return;
22072         }
22073         
22074         Roo.each(this.el.select('.bullet', true).elements, function(el){
22075             el.removeClass('selected');
22076         });
22077
22078         var bullet = this.el.select('.bullet-' + i, true).first();
22079         
22080         if(!bullet){
22081             return;
22082         }
22083         
22084         bullet.addClass('selected');
22085     }
22086     
22087     
22088   
22089 });
22090
22091  
22092
22093  
22094  
22095 Roo.apply(Roo.bootstrap.TabGroup, {
22096     
22097     groups: {},
22098      /**
22099     * register a Navigation Group
22100     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22101     */
22102     register : function(navgrp)
22103     {
22104         this.groups[navgrp.navId] = navgrp;
22105         
22106     },
22107     /**
22108     * fetch a Navigation Group based on the navigation ID
22109     * if one does not exist , it will get created.
22110     * @param {string} the navgroup to add
22111     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22112     */
22113     get: function(navId) {
22114         if (typeof(this.groups[navId]) == 'undefined') {
22115             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22116         }
22117         return this.groups[navId] ;
22118     }
22119     
22120     
22121     
22122 });
22123
22124  /*
22125  * - LGPL
22126  *
22127  * TabPanel
22128  * 
22129  */
22130
22131 /**
22132  * @class Roo.bootstrap.TabPanel
22133  * @extends Roo.bootstrap.Component
22134  * @children Roo.bootstrap.Component
22135  * Bootstrap TabPanel class
22136  * @cfg {Boolean} active panel active
22137  * @cfg {String} html panel content
22138  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22139  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22140  * @cfg {String} href click to link..
22141  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22142  * 
22143  * 
22144  * @constructor
22145  * Create a new TabPanel
22146  * @param {Object} config The config object
22147  */
22148
22149 Roo.bootstrap.TabPanel = function(config){
22150     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22151     this.addEvents({
22152         /**
22153              * @event changed
22154              * Fires when the active status changes
22155              * @param {Roo.bootstrap.TabPanel} this
22156              * @param {Boolean} state the new state
22157             
22158          */
22159         'changed': true,
22160         /**
22161              * @event beforedeactivate
22162              * Fires before a tab is de-activated - can be used to do validation on a form.
22163              * @param {Roo.bootstrap.TabPanel} this
22164              * @return {Boolean} false if there is an error
22165             
22166          */
22167         'beforedeactivate': true
22168      });
22169     
22170     this.tabId = this.tabId || Roo.id();
22171   
22172 };
22173
22174 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22175     
22176     active: false,
22177     html: false,
22178     tabId: false,
22179     navId : false,
22180     href : '',
22181     touchSlide : false,
22182     getAutoCreate : function(){
22183         
22184         
22185         var cfg = {
22186             tag: 'div',
22187             // item is needed for carousel - not sure if it has any effect otherwise
22188             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22189             html: this.html || ''
22190         };
22191         
22192         if(this.active){
22193             cfg.cls += ' active';
22194         }
22195         
22196         if(this.tabId){
22197             cfg.tabId = this.tabId;
22198         }
22199         
22200         
22201         
22202         return cfg;
22203     },
22204     
22205     initEvents:  function()
22206     {
22207         var p = this.parent();
22208         
22209         this.navId = this.navId || p.navId;
22210         
22211         if (typeof(this.navId) != 'undefined') {
22212             // not really needed.. but just in case.. parent should be a NavGroup.
22213             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22214             
22215             tg.register(this);
22216             
22217             var i = tg.tabs.length - 1;
22218             
22219             if(this.active && tg.bullets > 0 && i < tg.bullets){
22220                 tg.setActiveBullet(i);
22221             }
22222         }
22223         
22224         this.el.on('click', this.onClick, this);
22225         
22226         if(Roo.isTouch && this.touchSlide){
22227             this.el.on("touchstart", this.onTouchStart, this);
22228             this.el.on("touchmove", this.onTouchMove, this);
22229             this.el.on("touchend", this.onTouchEnd, this);
22230         }
22231         
22232     },
22233     
22234     onRender : function(ct, position)
22235     {
22236         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22237     },
22238     
22239     setActive : function(state)
22240     {
22241         Roo.log("panel - set active " + this.tabId + "=" + state);
22242         
22243         this.active = state;
22244         if (!state) {
22245             this.el.removeClass('active');
22246             
22247         } else  if (!this.el.hasClass('active')) {
22248             this.el.addClass('active');
22249         }
22250         
22251         this.fireEvent('changed', this, state);
22252     },
22253     
22254     onClick : function(e)
22255     {
22256         e.preventDefault();
22257         
22258         if(!this.href.length){
22259             return;
22260         }
22261         
22262         window.location.href = this.href;
22263     },
22264     
22265     startX : 0,
22266     startY : 0,
22267     endX : 0,
22268     endY : 0,
22269     swiping : false,
22270     
22271     onTouchStart : function(e)
22272     {
22273         this.swiping = false;
22274         
22275         this.startX = e.browserEvent.touches[0].clientX;
22276         this.startY = e.browserEvent.touches[0].clientY;
22277     },
22278     
22279     onTouchMove : function(e)
22280     {
22281         this.swiping = true;
22282         
22283         this.endX = e.browserEvent.touches[0].clientX;
22284         this.endY = e.browserEvent.touches[0].clientY;
22285     },
22286     
22287     onTouchEnd : function(e)
22288     {
22289         if(!this.swiping){
22290             this.onClick(e);
22291             return;
22292         }
22293         
22294         var tabGroup = this.parent();
22295         
22296         if(this.endX > this.startX){ // swiping right
22297             tabGroup.showPanelPrev();
22298             return;
22299         }
22300         
22301         if(this.startX > this.endX){ // swiping left
22302             tabGroup.showPanelNext();
22303             return;
22304         }
22305     }
22306     
22307     
22308 });
22309  
22310
22311  
22312
22313  /*
22314  * - LGPL
22315  *
22316  * DateField
22317  * 
22318  */
22319
22320 /**
22321  * @class Roo.bootstrap.DateField
22322  * @extends Roo.bootstrap.Input
22323  * Bootstrap DateField class
22324  * @cfg {Number} weekStart default 0
22325  * @cfg {String} viewMode default empty, (months|years)
22326  * @cfg {String} minViewMode default empty, (months|years)
22327  * @cfg {Number} startDate default -Infinity
22328  * @cfg {Number} endDate default Infinity
22329  * @cfg {Boolean} todayHighlight default false
22330  * @cfg {Boolean} todayBtn default false
22331  * @cfg {Boolean} calendarWeeks default false
22332  * @cfg {Object} daysOfWeekDisabled default empty
22333  * @cfg {Boolean} singleMode default false (true | false)
22334  * 
22335  * @cfg {Boolean} keyboardNavigation default true
22336  * @cfg {String} language default en
22337  * 
22338  * @constructor
22339  * Create a new DateField
22340  * @param {Object} config The config object
22341  */
22342
22343 Roo.bootstrap.DateField = function(config){
22344     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22345      this.addEvents({
22346             /**
22347              * @event show
22348              * Fires when this field show.
22349              * @param {Roo.bootstrap.DateField} this
22350              * @param {Mixed} date The date value
22351              */
22352             show : true,
22353             /**
22354              * @event show
22355              * Fires when this field hide.
22356              * @param {Roo.bootstrap.DateField} this
22357              * @param {Mixed} date The date value
22358              */
22359             hide : true,
22360             /**
22361              * @event select
22362              * Fires when select a date.
22363              * @param {Roo.bootstrap.DateField} this
22364              * @param {Mixed} date The date value
22365              */
22366             select : true,
22367             /**
22368              * @event beforeselect
22369              * Fires when before select a date.
22370              * @param {Roo.bootstrap.DateField} this
22371              * @param {Mixed} date The date value
22372              */
22373             beforeselect : true
22374         });
22375 };
22376
22377 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22378     
22379     /**
22380      * @cfg {String} format
22381      * The default date format string which can be overriden for localization support.  The format must be
22382      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22383      */
22384     format : "m/d/y",
22385     /**
22386      * @cfg {String} altFormats
22387      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22388      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22389      */
22390     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22391     
22392     weekStart : 0,
22393     
22394     viewMode : '',
22395     
22396     minViewMode : '',
22397     
22398     todayHighlight : false,
22399     
22400     todayBtn: false,
22401     
22402     language: 'en',
22403     
22404     keyboardNavigation: true,
22405     
22406     calendarWeeks: false,
22407     
22408     startDate: -Infinity,
22409     
22410     endDate: Infinity,
22411     
22412     daysOfWeekDisabled: [],
22413     
22414     _events: [],
22415     
22416     singleMode : false,
22417     
22418     UTCDate: function()
22419     {
22420         return new Date(Date.UTC.apply(Date, arguments));
22421     },
22422     
22423     UTCToday: function()
22424     {
22425         var today = new Date();
22426         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22427     },
22428     
22429     getDate: function() {
22430             var d = this.getUTCDate();
22431             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22432     },
22433     
22434     getUTCDate: function() {
22435             return this.date;
22436     },
22437     
22438     setDate: function(d) {
22439             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22440     },
22441     
22442     setUTCDate: function(d) {
22443             this.date = d;
22444             this.setValue(this.formatDate(this.date));
22445     },
22446         
22447     onRender: function(ct, position)
22448     {
22449         
22450         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22451         
22452         this.language = this.language || 'en';
22453         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22454         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22455         
22456         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22457         this.format = this.format || 'm/d/y';
22458         this.isInline = false;
22459         this.isInput = true;
22460         this.component = this.el.select('.add-on', true).first() || false;
22461         this.component = (this.component && this.component.length === 0) ? false : this.component;
22462         this.hasInput = this.component && this.inputEl().length;
22463         
22464         if (typeof(this.minViewMode === 'string')) {
22465             switch (this.minViewMode) {
22466                 case 'months':
22467                     this.minViewMode = 1;
22468                     break;
22469                 case 'years':
22470                     this.minViewMode = 2;
22471                     break;
22472                 default:
22473                     this.minViewMode = 0;
22474                     break;
22475             }
22476         }
22477         
22478         if (typeof(this.viewMode === 'string')) {
22479             switch (this.viewMode) {
22480                 case 'months':
22481                     this.viewMode = 1;
22482                     break;
22483                 case 'years':
22484                     this.viewMode = 2;
22485                     break;
22486                 default:
22487                     this.viewMode = 0;
22488                     break;
22489             }
22490         }
22491                 
22492         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22493         
22494 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22495         
22496         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22497         
22498         this.picker().on('mousedown', this.onMousedown, this);
22499         this.picker().on('click', this.onClick, this);
22500         
22501         this.picker().addClass('datepicker-dropdown');
22502         
22503         this.startViewMode = this.viewMode;
22504         
22505         if(this.singleMode){
22506             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22507                 v.setVisibilityMode(Roo.Element.DISPLAY);
22508                 v.hide();
22509             });
22510             
22511             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22512                 v.setStyle('width', '189px');
22513             });
22514         }
22515         
22516         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22517             if(!this.calendarWeeks){
22518                 v.remove();
22519                 return;
22520             }
22521             
22522             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22523             v.attr('colspan', function(i, val){
22524                 return parseInt(val) + 1;
22525             });
22526         });
22527                         
22528         
22529         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22530         
22531         this.setStartDate(this.startDate);
22532         this.setEndDate(this.endDate);
22533         
22534         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22535         
22536         this.fillDow();
22537         this.fillMonths();
22538         this.update();
22539         this.showMode();
22540         
22541         if(this.isInline) {
22542             this.showPopup();
22543         }
22544     },
22545     
22546     picker : function()
22547     {
22548         return this.pickerEl;
22549 //        return this.el.select('.datepicker', true).first();
22550     },
22551     
22552     fillDow: function()
22553     {
22554         var dowCnt = this.weekStart;
22555         
22556         var dow = {
22557             tag: 'tr',
22558             cn: [
22559                 
22560             ]
22561         };
22562         
22563         if(this.calendarWeeks){
22564             dow.cn.push({
22565                 tag: 'th',
22566                 cls: 'cw',
22567                 html: '&nbsp;'
22568             })
22569         }
22570         
22571         while (dowCnt < this.weekStart + 7) {
22572             dow.cn.push({
22573                 tag: 'th',
22574                 cls: 'dow',
22575                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22576             });
22577         }
22578         
22579         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22580     },
22581     
22582     fillMonths: function()
22583     {    
22584         var i = 0;
22585         var months = this.picker().select('>.datepicker-months td', true).first();
22586         
22587         months.dom.innerHTML = '';
22588         
22589         while (i < 12) {
22590             var month = {
22591                 tag: 'span',
22592                 cls: 'month',
22593                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22594             };
22595             
22596             months.createChild(month);
22597         }
22598         
22599     },
22600     
22601     update: function()
22602     {
22603         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;
22604         
22605         if (this.date < this.startDate) {
22606             this.viewDate = new Date(this.startDate);
22607         } else if (this.date > this.endDate) {
22608             this.viewDate = new Date(this.endDate);
22609         } else {
22610             this.viewDate = new Date(this.date);
22611         }
22612         
22613         this.fill();
22614     },
22615     
22616     fill: function() 
22617     {
22618         var d = new Date(this.viewDate),
22619                 year = d.getUTCFullYear(),
22620                 month = d.getUTCMonth(),
22621                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22622                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22623                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22624                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22625                 currentDate = this.date && this.date.valueOf(),
22626                 today = this.UTCToday();
22627         
22628         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22629         
22630 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22631         
22632 //        this.picker.select('>tfoot th.today').
22633 //                                              .text(dates[this.language].today)
22634 //                                              .toggle(this.todayBtn !== false);
22635     
22636         this.updateNavArrows();
22637         this.fillMonths();
22638                                                 
22639         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22640         
22641         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22642          
22643         prevMonth.setUTCDate(day);
22644         
22645         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22646         
22647         var nextMonth = new Date(prevMonth);
22648         
22649         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22650         
22651         nextMonth = nextMonth.valueOf();
22652         
22653         var fillMonths = false;
22654         
22655         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22656         
22657         while(prevMonth.valueOf() <= nextMonth) {
22658             var clsName = '';
22659             
22660             if (prevMonth.getUTCDay() === this.weekStart) {
22661                 if(fillMonths){
22662                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22663                 }
22664                     
22665                 fillMonths = {
22666                     tag: 'tr',
22667                     cn: []
22668                 };
22669                 
22670                 if(this.calendarWeeks){
22671                     // ISO 8601: First week contains first thursday.
22672                     // ISO also states week starts on Monday, but we can be more abstract here.
22673                     var
22674                     // Start of current week: based on weekstart/current date
22675                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22676                     // Thursday of this week
22677                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22678                     // First Thursday of year, year from thursday
22679                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22680                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22681                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22682                     
22683                     fillMonths.cn.push({
22684                         tag: 'td',
22685                         cls: 'cw',
22686                         html: calWeek
22687                     });
22688                 }
22689             }
22690             
22691             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22692                 clsName += ' old';
22693             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22694                 clsName += ' new';
22695             }
22696             if (this.todayHighlight &&
22697                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22698                 prevMonth.getUTCMonth() == today.getMonth() &&
22699                 prevMonth.getUTCDate() == today.getDate()) {
22700                 clsName += ' today';
22701             }
22702             
22703             if (currentDate && prevMonth.valueOf() === currentDate) {
22704                 clsName += ' active';
22705             }
22706             
22707             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22708                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22709                     clsName += ' disabled';
22710             }
22711             
22712             fillMonths.cn.push({
22713                 tag: 'td',
22714                 cls: 'day ' + clsName,
22715                 html: prevMonth.getDate()
22716             });
22717             
22718             prevMonth.setDate(prevMonth.getDate()+1);
22719         }
22720           
22721         var currentYear = this.date && this.date.getUTCFullYear();
22722         var currentMonth = this.date && this.date.getUTCMonth();
22723         
22724         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22725         
22726         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22727             v.removeClass('active');
22728             
22729             if(currentYear === year && k === currentMonth){
22730                 v.addClass('active');
22731             }
22732             
22733             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22734                 v.addClass('disabled');
22735             }
22736             
22737         });
22738         
22739         
22740         year = parseInt(year/10, 10) * 10;
22741         
22742         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22743         
22744         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22745         
22746         year -= 1;
22747         for (var i = -1; i < 11; i++) {
22748             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22749                 tag: 'span',
22750                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22751                 html: year
22752             });
22753             
22754             year += 1;
22755         }
22756     },
22757     
22758     showMode: function(dir) 
22759     {
22760         if (dir) {
22761             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22762         }
22763         
22764         Roo.each(this.picker().select('>div',true).elements, function(v){
22765             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22766             v.hide();
22767         });
22768         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22769     },
22770     
22771     place: function()
22772     {
22773         if(this.isInline) {
22774             return;
22775         }
22776         
22777         this.picker().removeClass(['bottom', 'top']);
22778         
22779         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22780             /*
22781              * place to the top of element!
22782              *
22783              */
22784             
22785             this.picker().addClass('top');
22786             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22787             
22788             return;
22789         }
22790         
22791         this.picker().addClass('bottom');
22792         
22793         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22794     },
22795     
22796     parseDate : function(value)
22797     {
22798         if(!value || value instanceof Date){
22799             return value;
22800         }
22801         var v = Date.parseDate(value, this.format);
22802         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22803             v = Date.parseDate(value, 'Y-m-d');
22804         }
22805         if(!v && this.altFormats){
22806             if(!this.altFormatsArray){
22807                 this.altFormatsArray = this.altFormats.split("|");
22808             }
22809             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22810                 v = Date.parseDate(value, this.altFormatsArray[i]);
22811             }
22812         }
22813         return v;
22814     },
22815     
22816     formatDate : function(date, fmt)
22817     {   
22818         return (!date || !(date instanceof Date)) ?
22819         date : date.dateFormat(fmt || this.format);
22820     },
22821     
22822     onFocus : function()
22823     {
22824         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22825         this.showPopup();
22826     },
22827     
22828     onBlur : function()
22829     {
22830         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22831         
22832         var d = this.inputEl().getValue();
22833         
22834         this.setValue(d);
22835                 
22836         this.hidePopup();
22837     },
22838     
22839     showPopup : function()
22840     {
22841         this.picker().show();
22842         this.update();
22843         this.place();
22844         
22845         this.fireEvent('showpopup', this, this.date);
22846     },
22847     
22848     hidePopup : function()
22849     {
22850         if(this.isInline) {
22851             return;
22852         }
22853         this.picker().hide();
22854         this.viewMode = this.startViewMode;
22855         this.showMode();
22856         
22857         this.fireEvent('hidepopup', this, this.date);
22858         
22859     },
22860     
22861     onMousedown: function(e)
22862     {
22863         e.stopPropagation();
22864         e.preventDefault();
22865     },
22866     
22867     keyup: function(e)
22868     {
22869         Roo.bootstrap.DateField.superclass.keyup.call(this);
22870         this.update();
22871     },
22872
22873     setValue: function(v)
22874     {
22875         if(this.fireEvent('beforeselect', this, v) !== false){
22876             var d = new Date(this.parseDate(v) ).clearTime();
22877         
22878             if(isNaN(d.getTime())){
22879                 this.date = this.viewDate = '';
22880                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22881                 return;
22882             }
22883
22884             v = this.formatDate(d);
22885
22886             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22887
22888             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22889
22890             this.update();
22891
22892             this.fireEvent('select', this, this.date);
22893         }
22894     },
22895     
22896     getValue: function()
22897     {
22898         return this.formatDate(this.date);
22899     },
22900     
22901     fireKey: function(e)
22902     {
22903         if (!this.picker().isVisible()){
22904             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22905                 this.showPopup();
22906             }
22907             return;
22908         }
22909         
22910         var dateChanged = false,
22911         dir, day, month,
22912         newDate, newViewDate;
22913         
22914         switch(e.keyCode){
22915             case 27: // escape
22916                 this.hidePopup();
22917                 e.preventDefault();
22918                 break;
22919             case 37: // left
22920             case 39: // right
22921                 if (!this.keyboardNavigation) {
22922                     break;
22923                 }
22924                 dir = e.keyCode == 37 ? -1 : 1;
22925                 
22926                 if (e.ctrlKey){
22927                     newDate = this.moveYear(this.date, dir);
22928                     newViewDate = this.moveYear(this.viewDate, dir);
22929                 } else if (e.shiftKey){
22930                     newDate = this.moveMonth(this.date, dir);
22931                     newViewDate = this.moveMonth(this.viewDate, dir);
22932                 } else {
22933                     newDate = new Date(this.date);
22934                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22935                     newViewDate = new Date(this.viewDate);
22936                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22937                 }
22938                 if (this.dateWithinRange(newDate)){
22939                     this.date = newDate;
22940                     this.viewDate = newViewDate;
22941                     this.setValue(this.formatDate(this.date));
22942 //                    this.update();
22943                     e.preventDefault();
22944                     dateChanged = true;
22945                 }
22946                 break;
22947             case 38: // up
22948             case 40: // down
22949                 if (!this.keyboardNavigation) {
22950                     break;
22951                 }
22952                 dir = e.keyCode == 38 ? -1 : 1;
22953                 if (e.ctrlKey){
22954                     newDate = this.moveYear(this.date, dir);
22955                     newViewDate = this.moveYear(this.viewDate, dir);
22956                 } else if (e.shiftKey){
22957                     newDate = this.moveMonth(this.date, dir);
22958                     newViewDate = this.moveMonth(this.viewDate, dir);
22959                 } else {
22960                     newDate = new Date(this.date);
22961                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22962                     newViewDate = new Date(this.viewDate);
22963                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22964                 }
22965                 if (this.dateWithinRange(newDate)){
22966                     this.date = newDate;
22967                     this.viewDate = newViewDate;
22968                     this.setValue(this.formatDate(this.date));
22969 //                    this.update();
22970                     e.preventDefault();
22971                     dateChanged = true;
22972                 }
22973                 break;
22974             case 13: // enter
22975                 this.setValue(this.formatDate(this.date));
22976                 this.hidePopup();
22977                 e.preventDefault();
22978                 break;
22979             case 9: // tab
22980                 this.setValue(this.formatDate(this.date));
22981                 this.hidePopup();
22982                 break;
22983             case 16: // shift
22984             case 17: // ctrl
22985             case 18: // alt
22986                 break;
22987             default :
22988                 this.hidePopup();
22989                 
22990         }
22991     },
22992     
22993     
22994     onClick: function(e) 
22995     {
22996         e.stopPropagation();
22997         e.preventDefault();
22998         
22999         var target = e.getTarget();
23000         
23001         if(target.nodeName.toLowerCase() === 'i'){
23002             target = Roo.get(target).dom.parentNode;
23003         }
23004         
23005         var nodeName = target.nodeName;
23006         var className = target.className;
23007         var html = target.innerHTML;
23008         //Roo.log(nodeName);
23009         
23010         switch(nodeName.toLowerCase()) {
23011             case 'th':
23012                 switch(className) {
23013                     case 'switch':
23014                         this.showMode(1);
23015                         break;
23016                     case 'prev':
23017                     case 'next':
23018                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23019                         switch(this.viewMode){
23020                                 case 0:
23021                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23022                                         break;
23023                                 case 1:
23024                                 case 2:
23025                                         this.viewDate = this.moveYear(this.viewDate, dir);
23026                                         break;
23027                         }
23028                         this.fill();
23029                         break;
23030                     case 'today':
23031                         var date = new Date();
23032                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23033 //                        this.fill()
23034                         this.setValue(this.formatDate(this.date));
23035                         
23036                         this.hidePopup();
23037                         break;
23038                 }
23039                 break;
23040             case 'span':
23041                 if (className.indexOf('disabled') < 0) {
23042                 if (!this.viewDate) {
23043                     this.viewDate = new Date();
23044                 }
23045                 this.viewDate.setUTCDate(1);
23046                     if (className.indexOf('month') > -1) {
23047                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23048                     } else {
23049                         var year = parseInt(html, 10) || 0;
23050                         this.viewDate.setUTCFullYear(year);
23051                         
23052                     }
23053                     
23054                     if(this.singleMode){
23055                         this.setValue(this.formatDate(this.viewDate));
23056                         this.hidePopup();
23057                         return;
23058                     }
23059                     
23060                     this.showMode(-1);
23061                     this.fill();
23062                 }
23063                 break;
23064                 
23065             case 'td':
23066                 //Roo.log(className);
23067                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23068                     var day = parseInt(html, 10) || 1;
23069                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23070                         month = (this.viewDate || new Date()).getUTCMonth();
23071
23072                     if (className.indexOf('old') > -1) {
23073                         if(month === 0 ){
23074                             month = 11;
23075                             year -= 1;
23076                         }else{
23077                             month -= 1;
23078                         }
23079                     } else if (className.indexOf('new') > -1) {
23080                         if (month == 11) {
23081                             month = 0;
23082                             year += 1;
23083                         } else {
23084                             month += 1;
23085                         }
23086                     }
23087                     //Roo.log([year,month,day]);
23088                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23089                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23090 //                    this.fill();
23091                     //Roo.log(this.formatDate(this.date));
23092                     this.setValue(this.formatDate(this.date));
23093                     this.hidePopup();
23094                 }
23095                 break;
23096         }
23097     },
23098     
23099     setStartDate: function(startDate)
23100     {
23101         this.startDate = startDate || -Infinity;
23102         if (this.startDate !== -Infinity) {
23103             this.startDate = this.parseDate(this.startDate);
23104         }
23105         this.update();
23106         this.updateNavArrows();
23107     },
23108
23109     setEndDate: function(endDate)
23110     {
23111         this.endDate = endDate || Infinity;
23112         if (this.endDate !== Infinity) {
23113             this.endDate = this.parseDate(this.endDate);
23114         }
23115         this.update();
23116         this.updateNavArrows();
23117     },
23118     
23119     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23120     {
23121         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23122         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23123             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23124         }
23125         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23126             return parseInt(d, 10);
23127         });
23128         this.update();
23129         this.updateNavArrows();
23130     },
23131     
23132     updateNavArrows: function() 
23133     {
23134         if(this.singleMode){
23135             return;
23136         }
23137         
23138         var d = new Date(this.viewDate),
23139         year = d.getUTCFullYear(),
23140         month = d.getUTCMonth();
23141         
23142         Roo.each(this.picker().select('.prev', true).elements, function(v){
23143             v.show();
23144             switch (this.viewMode) {
23145                 case 0:
23146
23147                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23148                         v.hide();
23149                     }
23150                     break;
23151                 case 1:
23152                 case 2:
23153                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23154                         v.hide();
23155                     }
23156                     break;
23157             }
23158         });
23159         
23160         Roo.each(this.picker().select('.next', true).elements, function(v){
23161             v.show();
23162             switch (this.viewMode) {
23163                 case 0:
23164
23165                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23166                         v.hide();
23167                     }
23168                     break;
23169                 case 1:
23170                 case 2:
23171                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23172                         v.hide();
23173                     }
23174                     break;
23175             }
23176         })
23177     },
23178     
23179     moveMonth: function(date, dir)
23180     {
23181         if (!dir) {
23182             return date;
23183         }
23184         var new_date = new Date(date.valueOf()),
23185         day = new_date.getUTCDate(),
23186         month = new_date.getUTCMonth(),
23187         mag = Math.abs(dir),
23188         new_month, test;
23189         dir = dir > 0 ? 1 : -1;
23190         if (mag == 1){
23191             test = dir == -1
23192             // If going back one month, make sure month is not current month
23193             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23194             ? function(){
23195                 return new_date.getUTCMonth() == month;
23196             }
23197             // If going forward one month, make sure month is as expected
23198             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23199             : function(){
23200                 return new_date.getUTCMonth() != new_month;
23201             };
23202             new_month = month + dir;
23203             new_date.setUTCMonth(new_month);
23204             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23205             if (new_month < 0 || new_month > 11) {
23206                 new_month = (new_month + 12) % 12;
23207             }
23208         } else {
23209             // For magnitudes >1, move one month at a time...
23210             for (var i=0; i<mag; i++) {
23211                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23212                 new_date = this.moveMonth(new_date, dir);
23213             }
23214             // ...then reset the day, keeping it in the new month
23215             new_month = new_date.getUTCMonth();
23216             new_date.setUTCDate(day);
23217             test = function(){
23218                 return new_month != new_date.getUTCMonth();
23219             };
23220         }
23221         // Common date-resetting loop -- if date is beyond end of month, make it
23222         // end of month
23223         while (test()){
23224             new_date.setUTCDate(--day);
23225             new_date.setUTCMonth(new_month);
23226         }
23227         return new_date;
23228     },
23229
23230     moveYear: function(date, dir)
23231     {
23232         return this.moveMonth(date, dir*12);
23233     },
23234
23235     dateWithinRange: function(date)
23236     {
23237         return date >= this.startDate && date <= this.endDate;
23238     },
23239
23240     
23241     remove: function() 
23242     {
23243         this.picker().remove();
23244     },
23245     
23246     validateValue : function(value)
23247     {
23248         if(this.getVisibilityEl().hasClass('hidden')){
23249             return true;
23250         }
23251         
23252         if(value.length < 1)  {
23253             if(this.allowBlank){
23254                 return true;
23255             }
23256             return false;
23257         }
23258         
23259         if(value.length < this.minLength){
23260             return false;
23261         }
23262         if(value.length > this.maxLength){
23263             return false;
23264         }
23265         if(this.vtype){
23266             var vt = Roo.form.VTypes;
23267             if(!vt[this.vtype](value, this)){
23268                 return false;
23269             }
23270         }
23271         if(typeof this.validator == "function"){
23272             var msg = this.validator(value);
23273             if(msg !== true){
23274                 return false;
23275             }
23276         }
23277         
23278         if(this.regex && !this.regex.test(value)){
23279             return false;
23280         }
23281         
23282         if(typeof(this.parseDate(value)) == 'undefined'){
23283             return false;
23284         }
23285         
23286         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23287             return false;
23288         }      
23289         
23290         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23291             return false;
23292         } 
23293         
23294         
23295         return true;
23296     },
23297     
23298     reset : function()
23299     {
23300         this.date = this.viewDate = '';
23301         
23302         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23303     }
23304    
23305 });
23306
23307 Roo.apply(Roo.bootstrap.DateField,  {
23308     
23309     head : {
23310         tag: 'thead',
23311         cn: [
23312         {
23313             tag: 'tr',
23314             cn: [
23315             {
23316                 tag: 'th',
23317                 cls: 'prev',
23318                 html: '<i class="fa fa-arrow-left"/>'
23319             },
23320             {
23321                 tag: 'th',
23322                 cls: 'switch',
23323                 colspan: '5'
23324             },
23325             {
23326                 tag: 'th',
23327                 cls: 'next',
23328                 html: '<i class="fa fa-arrow-right"/>'
23329             }
23330
23331             ]
23332         }
23333         ]
23334     },
23335     
23336     content : {
23337         tag: 'tbody',
23338         cn: [
23339         {
23340             tag: 'tr',
23341             cn: [
23342             {
23343                 tag: 'td',
23344                 colspan: '7'
23345             }
23346             ]
23347         }
23348         ]
23349     },
23350     
23351     footer : {
23352         tag: 'tfoot',
23353         cn: [
23354         {
23355             tag: 'tr',
23356             cn: [
23357             {
23358                 tag: 'th',
23359                 colspan: '7',
23360                 cls: 'today'
23361             }
23362                     
23363             ]
23364         }
23365         ]
23366     },
23367     
23368     dates:{
23369         en: {
23370             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23371             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23372             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23373             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23374             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23375             today: "Today"
23376         }
23377     },
23378     
23379     modes: [
23380     {
23381         clsName: 'days',
23382         navFnc: 'Month',
23383         navStep: 1
23384     },
23385     {
23386         clsName: 'months',
23387         navFnc: 'FullYear',
23388         navStep: 1
23389     },
23390     {
23391         clsName: 'years',
23392         navFnc: 'FullYear',
23393         navStep: 10
23394     }]
23395 });
23396
23397 Roo.apply(Roo.bootstrap.DateField,  {
23398   
23399     template : {
23400         tag: 'div',
23401         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23402         cn: [
23403         {
23404             tag: 'div',
23405             cls: 'datepicker-days',
23406             cn: [
23407             {
23408                 tag: 'table',
23409                 cls: 'table-condensed',
23410                 cn:[
23411                 Roo.bootstrap.DateField.head,
23412                 {
23413                     tag: 'tbody'
23414                 },
23415                 Roo.bootstrap.DateField.footer
23416                 ]
23417             }
23418             ]
23419         },
23420         {
23421             tag: 'div',
23422             cls: 'datepicker-months',
23423             cn: [
23424             {
23425                 tag: 'table',
23426                 cls: 'table-condensed',
23427                 cn:[
23428                 Roo.bootstrap.DateField.head,
23429                 Roo.bootstrap.DateField.content,
23430                 Roo.bootstrap.DateField.footer
23431                 ]
23432             }
23433             ]
23434         },
23435         {
23436             tag: 'div',
23437             cls: 'datepicker-years',
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     }
23452 });
23453
23454  
23455
23456  /*
23457  * - LGPL
23458  *
23459  * TimeField
23460  * 
23461  */
23462
23463 /**
23464  * @class Roo.bootstrap.TimeField
23465  * @extends Roo.bootstrap.Input
23466  * Bootstrap DateField class
23467  * 
23468  * 
23469  * @constructor
23470  * Create a new TimeField
23471  * @param {Object} config The config object
23472  */
23473
23474 Roo.bootstrap.TimeField = function(config){
23475     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23476     this.addEvents({
23477             /**
23478              * @event show
23479              * Fires when this field show.
23480              * @param {Roo.bootstrap.DateField} thisthis
23481              * @param {Mixed} date The date value
23482              */
23483             show : true,
23484             /**
23485              * @event show
23486              * Fires when this field hide.
23487              * @param {Roo.bootstrap.DateField} this
23488              * @param {Mixed} date The date value
23489              */
23490             hide : true,
23491             /**
23492              * @event select
23493              * Fires when select a date.
23494              * @param {Roo.bootstrap.DateField} this
23495              * @param {Mixed} date The date value
23496              */
23497             select : true
23498         });
23499 };
23500
23501 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23502     
23503     /**
23504      * @cfg {String} format
23505      * The default time format string which can be overriden for localization support.  The format must be
23506      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23507      */
23508     format : "H:i",
23509
23510     getAutoCreate : function()
23511     {
23512         this.after = '<i class="fa far fa-clock"></i>';
23513         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23514         
23515          
23516     },
23517     onRender: function(ct, position)
23518     {
23519         
23520         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23521                 
23522         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23523         
23524         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23525         
23526         this.pop = this.picker().select('>.datepicker-time',true).first();
23527         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23528         
23529         this.picker().on('mousedown', this.onMousedown, this);
23530         this.picker().on('click', this.onClick, this);
23531         
23532         this.picker().addClass('datepicker-dropdown');
23533     
23534         this.fillTime();
23535         this.update();
23536             
23537         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23538         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23539         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23540         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23541         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23542         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23543
23544     },
23545     
23546     fireKey: function(e){
23547         if (!this.picker().isVisible()){
23548             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23549                 this.show();
23550             }
23551             return;
23552         }
23553
23554         e.preventDefault();
23555         
23556         switch(e.keyCode){
23557             case 27: // escape
23558                 this.hide();
23559                 break;
23560             case 37: // left
23561             case 39: // right
23562                 this.onTogglePeriod();
23563                 break;
23564             case 38: // up
23565                 this.onIncrementMinutes();
23566                 break;
23567             case 40: // down
23568                 this.onDecrementMinutes();
23569                 break;
23570             case 13: // enter
23571             case 9: // tab
23572                 this.setTime();
23573                 break;
23574         }
23575     },
23576     
23577     onClick: function(e) {
23578         e.stopPropagation();
23579         e.preventDefault();
23580     },
23581     
23582     picker : function()
23583     {
23584         return this.pickerEl;
23585     },
23586     
23587     fillTime: function()
23588     {    
23589         var time = this.pop.select('tbody', true).first();
23590         
23591         time.dom.innerHTML = '';
23592         
23593         time.createChild({
23594             tag: 'tr',
23595             cn: [
23596                 {
23597                     tag: 'td',
23598                     cn: [
23599                         {
23600                             tag: 'a',
23601                             href: '#',
23602                             cls: 'btn',
23603                             cn: [
23604                                 {
23605                                     tag: 'i',
23606                                     cls: 'hours-up fa fas fa-chevron-up'
23607                                 }
23608                             ]
23609                         } 
23610                     ]
23611                 },
23612                 {
23613                     tag: 'td',
23614                     cls: 'separator'
23615                 },
23616                 {
23617                     tag: 'td',
23618                     cn: [
23619                         {
23620                             tag: 'a',
23621                             href: '#',
23622                             cls: 'btn',
23623                             cn: [
23624                                 {
23625                                     tag: 'i',
23626                                     cls: 'minutes-up fa fas fa-chevron-up'
23627                                 }
23628                             ]
23629                         }
23630                     ]
23631                 },
23632                 {
23633                     tag: 'td',
23634                     cls: 'separator'
23635                 }
23636             ]
23637         });
23638         
23639         time.createChild({
23640             tag: 'tr',
23641             cn: [
23642                 {
23643                     tag: 'td',
23644                     cn: [
23645                         {
23646                             tag: 'span',
23647                             cls: 'timepicker-hour',
23648                             html: '00'
23649                         }  
23650                     ]
23651                 },
23652                 {
23653                     tag: 'td',
23654                     cls: 'separator',
23655                     html: ':'
23656                 },
23657                 {
23658                     tag: 'td',
23659                     cn: [
23660                         {
23661                             tag: 'span',
23662                             cls: 'timepicker-minute',
23663                             html: '00'
23664                         }  
23665                     ]
23666                 },
23667                 {
23668                     tag: 'td',
23669                     cls: 'separator'
23670                 },
23671                 {
23672                     tag: 'td',
23673                     cn: [
23674                         {
23675                             tag: 'button',
23676                             type: 'button',
23677                             cls: 'btn btn-primary period',
23678                             html: 'AM'
23679                             
23680                         }
23681                     ]
23682                 }
23683             ]
23684         });
23685         
23686         time.createChild({
23687             tag: 'tr',
23688             cn: [
23689                 {
23690                     tag: 'td',
23691                     cn: [
23692                         {
23693                             tag: 'a',
23694                             href: '#',
23695                             cls: 'btn',
23696                             cn: [
23697                                 {
23698                                     tag: 'span',
23699                                     cls: 'hours-down fa fas fa-chevron-down'
23700                                 }
23701                             ]
23702                         }
23703                     ]
23704                 },
23705                 {
23706                     tag: 'td',
23707                     cls: 'separator'
23708                 },
23709                 {
23710                     tag: 'td',
23711                     cn: [
23712                         {
23713                             tag: 'a',
23714                             href: '#',
23715                             cls: 'btn',
23716                             cn: [
23717                                 {
23718                                     tag: 'span',
23719                                     cls: 'minutes-down fa fas fa-chevron-down'
23720                                 }
23721                             ]
23722                         }
23723                     ]
23724                 },
23725                 {
23726                     tag: 'td',
23727                     cls: 'separator'
23728                 }
23729             ]
23730         });
23731         
23732     },
23733     
23734     update: function()
23735     {
23736         
23737         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23738         
23739         this.fill();
23740     },
23741     
23742     fill: function() 
23743     {
23744         var hours = this.time.getHours();
23745         var minutes = this.time.getMinutes();
23746         var period = 'AM';
23747         
23748         if(hours > 11){
23749             period = 'PM';
23750         }
23751         
23752         if(hours == 0){
23753             hours = 12;
23754         }
23755         
23756         
23757         if(hours > 12){
23758             hours = hours - 12;
23759         }
23760         
23761         if(hours < 10){
23762             hours = '0' + hours;
23763         }
23764         
23765         if(minutes < 10){
23766             minutes = '0' + minutes;
23767         }
23768         
23769         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23770         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23771         this.pop.select('button', true).first().dom.innerHTML = period;
23772         
23773     },
23774     
23775     place: function()
23776     {   
23777         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23778         
23779         var cls = ['bottom'];
23780         
23781         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23782             cls.pop();
23783             cls.push('top');
23784         }
23785         
23786         cls.push('right');
23787         
23788         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23789             cls.pop();
23790             cls.push('left');
23791         }
23792         //this.picker().setXY(20000,20000);
23793         this.picker().addClass(cls.join('-'));
23794         
23795         var _this = this;
23796         
23797         Roo.each(cls, function(c){
23798             if(c == 'bottom'){
23799                 (function() {
23800                  //  
23801                 }).defer(200);
23802                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23803                 //_this.picker().setTop(_this.inputEl().getHeight());
23804                 return;
23805             }
23806             if(c == 'top'){
23807                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23808                 
23809                 //_this.picker().setTop(0 - _this.picker().getHeight());
23810                 return;
23811             }
23812             /*
23813             if(c == 'left'){
23814                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23815                 return;
23816             }
23817             if(c == 'right'){
23818                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23819                 return;
23820             }
23821             */
23822         });
23823         
23824     },
23825   
23826     onFocus : function()
23827     {
23828         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23829         this.show();
23830     },
23831     
23832     onBlur : function()
23833     {
23834         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23835         this.hide();
23836     },
23837     
23838     show : function()
23839     {
23840         this.picker().show();
23841         this.pop.show();
23842         this.update();
23843         this.place();
23844         
23845         this.fireEvent('show', this, this.date);
23846     },
23847     
23848     hide : function()
23849     {
23850         this.picker().hide();
23851         this.pop.hide();
23852         
23853         this.fireEvent('hide', this, this.date);
23854     },
23855     
23856     setTime : function()
23857     {
23858         this.hide();
23859         this.setValue(this.time.format(this.format));
23860         
23861         this.fireEvent('select', this, this.date);
23862         
23863         
23864     },
23865     
23866     onMousedown: function(e){
23867         e.stopPropagation();
23868         e.preventDefault();
23869     },
23870     
23871     onIncrementHours: function()
23872     {
23873         Roo.log('onIncrementHours');
23874         this.time = this.time.add(Date.HOUR, 1);
23875         this.update();
23876         
23877     },
23878     
23879     onDecrementHours: function()
23880     {
23881         Roo.log('onDecrementHours');
23882         this.time = this.time.add(Date.HOUR, -1);
23883         this.update();
23884     },
23885     
23886     onIncrementMinutes: function()
23887     {
23888         Roo.log('onIncrementMinutes');
23889         this.time = this.time.add(Date.MINUTE, 1);
23890         this.update();
23891     },
23892     
23893     onDecrementMinutes: function()
23894     {
23895         Roo.log('onDecrementMinutes');
23896         this.time = this.time.add(Date.MINUTE, -1);
23897         this.update();
23898     },
23899     
23900     onTogglePeriod: function()
23901     {
23902         Roo.log('onTogglePeriod');
23903         this.time = this.time.add(Date.HOUR, 12);
23904         this.update();
23905     }
23906     
23907    
23908 });
23909  
23910
23911 Roo.apply(Roo.bootstrap.TimeField,  {
23912   
23913     template : {
23914         tag: 'div',
23915         cls: 'datepicker dropdown-menu',
23916         cn: [
23917             {
23918                 tag: 'div',
23919                 cls: 'datepicker-time',
23920                 cn: [
23921                 {
23922                     tag: 'table',
23923                     cls: 'table-condensed',
23924                     cn:[
23925                         {
23926                             tag: 'tbody',
23927                             cn: [
23928                                 {
23929                                     tag: 'tr',
23930                                     cn: [
23931                                     {
23932                                         tag: 'td',
23933                                         colspan: '7'
23934                                     }
23935                                     ]
23936                                 }
23937                             ]
23938                         },
23939                         {
23940                             tag: 'tfoot',
23941                             cn: [
23942                                 {
23943                                     tag: 'tr',
23944                                     cn: [
23945                                     {
23946                                         tag: 'th',
23947                                         colspan: '7',
23948                                         cls: '',
23949                                         cn: [
23950                                             {
23951                                                 tag: 'button',
23952                                                 cls: 'btn btn-info ok',
23953                                                 html: 'OK'
23954                                             }
23955                                         ]
23956                                     }
23957                     
23958                                     ]
23959                                 }
23960                             ]
23961                         }
23962                     ]
23963                 }
23964                 ]
23965             }
23966         ]
23967     }
23968 });
23969
23970  
23971
23972  /*
23973  * - LGPL
23974  *
23975  * MonthField
23976  * 
23977  */
23978
23979 /**
23980  * @class Roo.bootstrap.MonthField
23981  * @extends Roo.bootstrap.Input
23982  * Bootstrap MonthField class
23983  * 
23984  * @cfg {String} language default en
23985  * 
23986  * @constructor
23987  * Create a new MonthField
23988  * @param {Object} config The config object
23989  */
23990
23991 Roo.bootstrap.MonthField = function(config){
23992     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23993     
23994     this.addEvents({
23995         /**
23996          * @event show
23997          * Fires when this field show.
23998          * @param {Roo.bootstrap.MonthField} this
23999          * @param {Mixed} date The date value
24000          */
24001         show : true,
24002         /**
24003          * @event show
24004          * Fires when this field hide.
24005          * @param {Roo.bootstrap.MonthField} this
24006          * @param {Mixed} date The date value
24007          */
24008         hide : true,
24009         /**
24010          * @event select
24011          * Fires when select a date.
24012          * @param {Roo.bootstrap.MonthField} this
24013          * @param {String} oldvalue The old value
24014          * @param {String} newvalue The new value
24015          */
24016         select : true
24017     });
24018 };
24019
24020 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24021     
24022     onRender: function(ct, position)
24023     {
24024         
24025         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24026         
24027         this.language = this.language || 'en';
24028         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24029         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24030         
24031         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24032         this.isInline = false;
24033         this.isInput = true;
24034         this.component = this.el.select('.add-on', true).first() || false;
24035         this.component = (this.component && this.component.length === 0) ? false : this.component;
24036         this.hasInput = this.component && this.inputEL().length;
24037         
24038         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24039         
24040         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24041         
24042         this.picker().on('mousedown', this.onMousedown, this);
24043         this.picker().on('click', this.onClick, this);
24044         
24045         this.picker().addClass('datepicker-dropdown');
24046         
24047         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24048             v.setStyle('width', '189px');
24049         });
24050         
24051         this.fillMonths();
24052         
24053         this.update();
24054         
24055         if(this.isInline) {
24056             this.show();
24057         }
24058         
24059     },
24060     
24061     setValue: function(v, suppressEvent)
24062     {   
24063         var o = this.getValue();
24064         
24065         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24066         
24067         this.update();
24068
24069         if(suppressEvent !== true){
24070             this.fireEvent('select', this, o, v);
24071         }
24072         
24073     },
24074     
24075     getValue: function()
24076     {
24077         return this.value;
24078     },
24079     
24080     onClick: function(e) 
24081     {
24082         e.stopPropagation();
24083         e.preventDefault();
24084         
24085         var target = e.getTarget();
24086         
24087         if(target.nodeName.toLowerCase() === 'i'){
24088             target = Roo.get(target).dom.parentNode;
24089         }
24090         
24091         var nodeName = target.nodeName;
24092         var className = target.className;
24093         var html = target.innerHTML;
24094         
24095         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24096             return;
24097         }
24098         
24099         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24100         
24101         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24102         
24103         this.hide();
24104                         
24105     },
24106     
24107     picker : function()
24108     {
24109         return this.pickerEl;
24110     },
24111     
24112     fillMonths: function()
24113     {    
24114         var i = 0;
24115         var months = this.picker().select('>.datepicker-months td', true).first();
24116         
24117         months.dom.innerHTML = '';
24118         
24119         while (i < 12) {
24120             var month = {
24121                 tag: 'span',
24122                 cls: 'month',
24123                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24124             };
24125             
24126             months.createChild(month);
24127         }
24128         
24129     },
24130     
24131     update: function()
24132     {
24133         var _this = this;
24134         
24135         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24136             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24137         }
24138         
24139         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24140             e.removeClass('active');
24141             
24142             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24143                 e.addClass('active');
24144             }
24145         })
24146     },
24147     
24148     place: function()
24149     {
24150         if(this.isInline) {
24151             return;
24152         }
24153         
24154         this.picker().removeClass(['bottom', 'top']);
24155         
24156         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24157             /*
24158              * place to the top of element!
24159              *
24160              */
24161             
24162             this.picker().addClass('top');
24163             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24164             
24165             return;
24166         }
24167         
24168         this.picker().addClass('bottom');
24169         
24170         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24171     },
24172     
24173     onFocus : function()
24174     {
24175         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24176         this.show();
24177     },
24178     
24179     onBlur : function()
24180     {
24181         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24182         
24183         var d = this.inputEl().getValue();
24184         
24185         this.setValue(d);
24186                 
24187         this.hide();
24188     },
24189     
24190     show : function()
24191     {
24192         this.picker().show();
24193         this.picker().select('>.datepicker-months', true).first().show();
24194         this.update();
24195         this.place();
24196         
24197         this.fireEvent('show', this, this.date);
24198     },
24199     
24200     hide : function()
24201     {
24202         if(this.isInline) {
24203             return;
24204         }
24205         this.picker().hide();
24206         this.fireEvent('hide', this, this.date);
24207         
24208     },
24209     
24210     onMousedown: function(e)
24211     {
24212         e.stopPropagation();
24213         e.preventDefault();
24214     },
24215     
24216     keyup: function(e)
24217     {
24218         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24219         this.update();
24220     },
24221
24222     fireKey: function(e)
24223     {
24224         if (!this.picker().isVisible()){
24225             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24226                 this.show();
24227             }
24228             return;
24229         }
24230         
24231         var dir;
24232         
24233         switch(e.keyCode){
24234             case 27: // escape
24235                 this.hide();
24236                 e.preventDefault();
24237                 break;
24238             case 37: // left
24239             case 39: // right
24240                 dir = e.keyCode == 37 ? -1 : 1;
24241                 
24242                 this.vIndex = this.vIndex + dir;
24243                 
24244                 if(this.vIndex < 0){
24245                     this.vIndex = 0;
24246                 }
24247                 
24248                 if(this.vIndex > 11){
24249                     this.vIndex = 11;
24250                 }
24251                 
24252                 if(isNaN(this.vIndex)){
24253                     this.vIndex = 0;
24254                 }
24255                 
24256                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24257                 
24258                 break;
24259             case 38: // up
24260             case 40: // down
24261                 
24262                 dir = e.keyCode == 38 ? -1 : 1;
24263                 
24264                 this.vIndex = this.vIndex + dir * 4;
24265                 
24266                 if(this.vIndex < 0){
24267                     this.vIndex = 0;
24268                 }
24269                 
24270                 if(this.vIndex > 11){
24271                     this.vIndex = 11;
24272                 }
24273                 
24274                 if(isNaN(this.vIndex)){
24275                     this.vIndex = 0;
24276                 }
24277                 
24278                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24279                 break;
24280                 
24281             case 13: // enter
24282                 
24283                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24284                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24285                 }
24286                 
24287                 this.hide();
24288                 e.preventDefault();
24289                 break;
24290             case 9: // tab
24291                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24292                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24293                 }
24294                 this.hide();
24295                 break;
24296             case 16: // shift
24297             case 17: // ctrl
24298             case 18: // alt
24299                 break;
24300             default :
24301                 this.hide();
24302                 
24303         }
24304     },
24305     
24306     remove: function() 
24307     {
24308         this.picker().remove();
24309     }
24310    
24311 });
24312
24313 Roo.apply(Roo.bootstrap.MonthField,  {
24314     
24315     content : {
24316         tag: 'tbody',
24317         cn: [
24318         {
24319             tag: 'tr',
24320             cn: [
24321             {
24322                 tag: 'td',
24323                 colspan: '7'
24324             }
24325             ]
24326         }
24327         ]
24328     },
24329     
24330     dates:{
24331         en: {
24332             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24333             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24334         }
24335     }
24336 });
24337
24338 Roo.apply(Roo.bootstrap.MonthField,  {
24339   
24340     template : {
24341         tag: 'div',
24342         cls: 'datepicker dropdown-menu roo-dynamic',
24343         cn: [
24344             {
24345                 tag: 'div',
24346                 cls: 'datepicker-months',
24347                 cn: [
24348                 {
24349                     tag: 'table',
24350                     cls: 'table-condensed',
24351                     cn:[
24352                         Roo.bootstrap.DateField.content
24353                     ]
24354                 }
24355                 ]
24356             }
24357         ]
24358     }
24359 });
24360
24361  
24362
24363  
24364  /*
24365  * - LGPL
24366  *
24367  * CheckBox
24368  * 
24369  */
24370
24371 /**
24372  * @class Roo.bootstrap.CheckBox
24373  * @extends Roo.bootstrap.Input
24374  * Bootstrap CheckBox class
24375  * 
24376  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24377  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24378  * @cfg {String} boxLabel The text that appears beside the checkbox
24379  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24380  * @cfg {Boolean} checked initnal the element
24381  * @cfg {Boolean} inline inline the element (default false)
24382  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24383  * @cfg {String} tooltip label tooltip
24384  * 
24385  * @constructor
24386  * Create a new CheckBox
24387  * @param {Object} config The config object
24388  */
24389
24390 Roo.bootstrap.CheckBox = function(config){
24391     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24392    
24393     this.addEvents({
24394         /**
24395         * @event check
24396         * Fires when the element is checked or unchecked.
24397         * @param {Roo.bootstrap.CheckBox} this This input
24398         * @param {Boolean} checked The new checked value
24399         */
24400        check : true,
24401        /**
24402         * @event click
24403         * Fires when the element is click.
24404         * @param {Roo.bootstrap.CheckBox} this This input
24405         */
24406        click : true
24407     });
24408     
24409 };
24410
24411 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24412   
24413     inputType: 'checkbox',
24414     inputValue: 1,
24415     valueOff: 0,
24416     boxLabel: false,
24417     checked: false,
24418     weight : false,
24419     inline: false,
24420     tooltip : '',
24421     
24422     // checkbox success does not make any sense really.. 
24423     invalidClass : "",
24424     validClass : "",
24425     
24426     
24427     getAutoCreate : function()
24428     {
24429         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24430         
24431         var id = Roo.id();
24432         
24433         var cfg = {};
24434         
24435         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24436         
24437         if(this.inline){
24438             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24439         }
24440         
24441         var input =  {
24442             tag: 'input',
24443             id : id,
24444             type : this.inputType,
24445             value : this.inputValue,
24446             cls : 'roo-' + this.inputType, //'form-box',
24447             placeholder : this.placeholder || ''
24448             
24449         };
24450         
24451         if(this.inputType != 'radio'){
24452             var hidden =  {
24453                 tag: 'input',
24454                 type : 'hidden',
24455                 cls : 'roo-hidden-value',
24456                 value : this.checked ? this.inputValue : this.valueOff
24457             };
24458         }
24459         
24460             
24461         if (this.weight) { // Validity check?
24462             cfg.cls += " " + this.inputType + "-" + this.weight;
24463         }
24464         
24465         if (this.disabled) {
24466             input.disabled=true;
24467         }
24468         
24469         if(this.checked){
24470             input.checked = this.checked;
24471         }
24472         
24473         if (this.name) {
24474             
24475             input.name = this.name;
24476             
24477             if(this.inputType != 'radio'){
24478                 hidden.name = this.name;
24479                 input.name = '_hidden_' + this.name;
24480             }
24481         }
24482         
24483         if (this.size) {
24484             input.cls += ' input-' + this.size;
24485         }
24486         
24487         var settings=this;
24488         
24489         ['xs','sm','md','lg'].map(function(size){
24490             if (settings[size]) {
24491                 cfg.cls += ' col-' + size + '-' + settings[size];
24492             }
24493         });
24494         
24495         var inputblock = input;
24496          
24497         if (this.before || this.after) {
24498             
24499             inputblock = {
24500                 cls : 'input-group',
24501                 cn :  [] 
24502             };
24503             
24504             if (this.before) {
24505                 inputblock.cn.push({
24506                     tag :'span',
24507                     cls : 'input-group-addon',
24508                     html : this.before
24509                 });
24510             }
24511             
24512             inputblock.cn.push(input);
24513             
24514             if(this.inputType != 'radio'){
24515                 inputblock.cn.push(hidden);
24516             }
24517             
24518             if (this.after) {
24519                 inputblock.cn.push({
24520                     tag :'span',
24521                     cls : 'input-group-addon',
24522                     html : this.after
24523                 });
24524             }
24525             
24526         }
24527         var boxLabelCfg = false;
24528         
24529         if(this.boxLabel){
24530            
24531             boxLabelCfg = {
24532                 tag: 'label',
24533                 //'for': id, // box label is handled by onclick - so no for...
24534                 cls: 'box-label',
24535                 html: this.boxLabel
24536             };
24537             if(this.tooltip){
24538                 boxLabelCfg.tooltip = this.tooltip;
24539             }
24540              
24541         }
24542         
24543         
24544         if (align ==='left' && this.fieldLabel.length) {
24545 //                Roo.log("left and has label");
24546             cfg.cn = [
24547                 {
24548                     tag: 'label',
24549                     'for' :  id,
24550                     cls : 'control-label',
24551                     html : this.fieldLabel
24552                 },
24553                 {
24554                     cls : "", 
24555                     cn: [
24556                         inputblock
24557                     ]
24558                 }
24559             ];
24560             
24561             if (boxLabelCfg) {
24562                 cfg.cn[1].cn.push(boxLabelCfg);
24563             }
24564             
24565             if(this.labelWidth > 12){
24566                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24567             }
24568             
24569             if(this.labelWidth < 13 && this.labelmd == 0){
24570                 this.labelmd = this.labelWidth;
24571             }
24572             
24573             if(this.labellg > 0){
24574                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24575                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24576             }
24577             
24578             if(this.labelmd > 0){
24579                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24580                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24581             }
24582             
24583             if(this.labelsm > 0){
24584                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24585                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24586             }
24587             
24588             if(this.labelxs > 0){
24589                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24590                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24591             }
24592             
24593         } else if ( this.fieldLabel.length) {
24594 //                Roo.log(" label");
24595                 cfg.cn = [
24596                    
24597                     {
24598                         tag: this.boxLabel ? 'span' : 'label',
24599                         'for': id,
24600                         cls: 'control-label box-input-label',
24601                         //cls : 'input-group-addon',
24602                         html : this.fieldLabel
24603                     },
24604                     
24605                     inputblock
24606                     
24607                 ];
24608                 if (boxLabelCfg) {
24609                     cfg.cn.push(boxLabelCfg);
24610                 }
24611
24612         } else {
24613             
24614 //                Roo.log(" no label && no align");
24615                 cfg.cn = [  inputblock ] ;
24616                 if (boxLabelCfg) {
24617                     cfg.cn.push(boxLabelCfg);
24618                 }
24619
24620                 
24621         }
24622         
24623        
24624         
24625         if(this.inputType != 'radio'){
24626             cfg.cn.push(hidden);
24627         }
24628         
24629         return cfg;
24630         
24631     },
24632     
24633     /**
24634      * return the real input element.
24635      */
24636     inputEl: function ()
24637     {
24638         return this.el.select('input.roo-' + this.inputType,true).first();
24639     },
24640     hiddenEl: function ()
24641     {
24642         return this.el.select('input.roo-hidden-value',true).first();
24643     },
24644     
24645     labelEl: function()
24646     {
24647         return this.el.select('label.control-label',true).first();
24648     },
24649     /* depricated... */
24650     
24651     label: function()
24652     {
24653         return this.labelEl();
24654     },
24655     
24656     boxLabelEl: function()
24657     {
24658         return this.el.select('label.box-label',true).first();
24659     },
24660     
24661     initEvents : function()
24662     {
24663 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24664         
24665         this.inputEl().on('click', this.onClick,  this);
24666         
24667         if (this.boxLabel) { 
24668             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24669         }
24670         
24671         this.startValue = this.getValue();
24672         
24673         if(this.groupId){
24674             Roo.bootstrap.CheckBox.register(this);
24675         }
24676     },
24677     
24678     onClick : function(e)
24679     {   
24680         if(this.fireEvent('click', this, e) !== false){
24681             this.setChecked(!this.checked);
24682         }
24683         
24684     },
24685     
24686     setChecked : function(state,suppressEvent)
24687     {
24688         this.startValue = this.getValue();
24689
24690         if(this.inputType == 'radio'){
24691             
24692             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24693                 e.dom.checked = false;
24694             });
24695             
24696             this.inputEl().dom.checked = true;
24697             
24698             this.inputEl().dom.value = this.inputValue;
24699             
24700             if(suppressEvent !== true){
24701                 this.fireEvent('check', this, true);
24702             }
24703             
24704             this.validate();
24705             
24706             return;
24707         }
24708         
24709         this.checked = state;
24710         
24711         this.inputEl().dom.checked = state;
24712         
24713         
24714         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24715         
24716         if(suppressEvent !== true){
24717             this.fireEvent('check', this, state);
24718         }
24719         
24720         this.validate();
24721     },
24722     
24723     getValue : function()
24724     {
24725         if(this.inputType == 'radio'){
24726             return this.getGroupValue();
24727         }
24728         
24729         return this.hiddenEl().dom.value;
24730         
24731     },
24732     
24733     getGroupValue : function()
24734     {
24735         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24736             return '';
24737         }
24738         
24739         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24740     },
24741     
24742     setValue : function(v,suppressEvent)
24743     {
24744         if(this.inputType == 'radio'){
24745             this.setGroupValue(v, suppressEvent);
24746             return;
24747         }
24748         
24749         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24750         
24751         this.validate();
24752     },
24753     
24754     setGroupValue : function(v, suppressEvent)
24755     {
24756         this.startValue = this.getValue();
24757         
24758         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24759             e.dom.checked = false;
24760             
24761             if(e.dom.value == v){
24762                 e.dom.checked = true;
24763             }
24764         });
24765         
24766         if(suppressEvent !== true){
24767             this.fireEvent('check', this, true);
24768         }
24769
24770         this.validate();
24771         
24772         return;
24773     },
24774     
24775     validate : function()
24776     {
24777         if(this.getVisibilityEl().hasClass('hidden')){
24778             return true;
24779         }
24780         
24781         if(
24782                 this.disabled || 
24783                 (this.inputType == 'radio' && this.validateRadio()) ||
24784                 (this.inputType == 'checkbox' && this.validateCheckbox())
24785         ){
24786             this.markValid();
24787             return true;
24788         }
24789         
24790         this.markInvalid();
24791         return false;
24792     },
24793     
24794     validateRadio : function()
24795     {
24796         if(this.getVisibilityEl().hasClass('hidden')){
24797             return true;
24798         }
24799         
24800         if(this.allowBlank){
24801             return true;
24802         }
24803         
24804         var valid = false;
24805         
24806         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24807             if(!e.dom.checked){
24808                 return;
24809             }
24810             
24811             valid = true;
24812             
24813             return false;
24814         });
24815         
24816         return valid;
24817     },
24818     
24819     validateCheckbox : function()
24820     {
24821         if(!this.groupId){
24822             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24823             //return (this.getValue() == this.inputValue) ? true : false;
24824         }
24825         
24826         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24827         
24828         if(!group){
24829             return false;
24830         }
24831         
24832         var r = false;
24833         
24834         for(var i in group){
24835             if(group[i].el.isVisible(true)){
24836                 r = false;
24837                 break;
24838             }
24839             
24840             r = true;
24841         }
24842         
24843         for(var i in group){
24844             if(r){
24845                 break;
24846             }
24847             
24848             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24849         }
24850         
24851         return r;
24852     },
24853     
24854     /**
24855      * Mark this field as valid
24856      */
24857     markValid : function()
24858     {
24859         var _this = this;
24860         
24861         this.fireEvent('valid', this);
24862         
24863         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24864         
24865         if(this.groupId){
24866             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24867         }
24868         
24869         if(label){
24870             label.markValid();
24871         }
24872
24873         if(this.inputType == 'radio'){
24874             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24875                 var fg = e.findParent('.form-group', false, true);
24876                 if (Roo.bootstrap.version == 3) {
24877                     fg.removeClass([_this.invalidClass, _this.validClass]);
24878                     fg.addClass(_this.validClass);
24879                 } else {
24880                     fg.removeClass(['is-valid', 'is-invalid']);
24881                     fg.addClass('is-valid');
24882                 }
24883             });
24884             
24885             return;
24886         }
24887
24888         if(!this.groupId){
24889             var fg = this.el.findParent('.form-group', false, true);
24890             if (Roo.bootstrap.version == 3) {
24891                 fg.removeClass([this.invalidClass, this.validClass]);
24892                 fg.addClass(this.validClass);
24893             } else {
24894                 fg.removeClass(['is-valid', 'is-invalid']);
24895                 fg.addClass('is-valid');
24896             }
24897             return;
24898         }
24899         
24900         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24901         
24902         if(!group){
24903             return;
24904         }
24905         
24906         for(var i in group){
24907             var fg = group[i].el.findParent('.form-group', false, true);
24908             if (Roo.bootstrap.version == 3) {
24909                 fg.removeClass([this.invalidClass, this.validClass]);
24910                 fg.addClass(this.validClass);
24911             } else {
24912                 fg.removeClass(['is-valid', 'is-invalid']);
24913                 fg.addClass('is-valid');
24914             }
24915         }
24916     },
24917     
24918      /**
24919      * Mark this field as invalid
24920      * @param {String} msg The validation message
24921      */
24922     markInvalid : function(msg)
24923     {
24924         if(this.allowBlank){
24925             return;
24926         }
24927         
24928         var _this = this;
24929         
24930         this.fireEvent('invalid', this, msg);
24931         
24932         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24933         
24934         if(this.groupId){
24935             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24936         }
24937         
24938         if(label){
24939             label.markInvalid();
24940         }
24941             
24942         if(this.inputType == 'radio'){
24943             
24944             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24945                 var fg = e.findParent('.form-group', false, true);
24946                 if (Roo.bootstrap.version == 3) {
24947                     fg.removeClass([_this.invalidClass, _this.validClass]);
24948                     fg.addClass(_this.invalidClass);
24949                 } else {
24950                     fg.removeClass(['is-invalid', 'is-valid']);
24951                     fg.addClass('is-invalid');
24952                 }
24953             });
24954             
24955             return;
24956         }
24957         
24958         if(!this.groupId){
24959             var fg = this.el.findParent('.form-group', false, true);
24960             if (Roo.bootstrap.version == 3) {
24961                 fg.removeClass([_this.invalidClass, _this.validClass]);
24962                 fg.addClass(_this.invalidClass);
24963             } else {
24964                 fg.removeClass(['is-invalid', 'is-valid']);
24965                 fg.addClass('is-invalid');
24966             }
24967             return;
24968         }
24969         
24970         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24971         
24972         if(!group){
24973             return;
24974         }
24975         
24976         for(var i in group){
24977             var fg = group[i].el.findParent('.form-group', false, true);
24978             if (Roo.bootstrap.version == 3) {
24979                 fg.removeClass([_this.invalidClass, _this.validClass]);
24980                 fg.addClass(_this.invalidClass);
24981             } else {
24982                 fg.removeClass(['is-invalid', 'is-valid']);
24983                 fg.addClass('is-invalid');
24984             }
24985         }
24986         
24987     },
24988     
24989     clearInvalid : function()
24990     {
24991         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24992         
24993         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24994         
24995         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24996         
24997         if (label && label.iconEl) {
24998             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24999             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25000         }
25001     },
25002     
25003     disable : function()
25004     {
25005         if(this.inputType != 'radio'){
25006             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25007             return;
25008         }
25009         
25010         var _this = this;
25011         
25012         if(this.rendered){
25013             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25014                 _this.getActionEl().addClass(this.disabledClass);
25015                 e.dom.disabled = true;
25016             });
25017         }
25018         
25019         this.disabled = true;
25020         this.fireEvent("disable", this);
25021         return this;
25022     },
25023
25024     enable : function()
25025     {
25026         if(this.inputType != 'radio'){
25027             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25028             return;
25029         }
25030         
25031         var _this = this;
25032         
25033         if(this.rendered){
25034             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25035                 _this.getActionEl().removeClass(this.disabledClass);
25036                 e.dom.disabled = false;
25037             });
25038         }
25039         
25040         this.disabled = false;
25041         this.fireEvent("enable", this);
25042         return this;
25043     },
25044     
25045     setBoxLabel : function(v)
25046     {
25047         this.boxLabel = v;
25048         
25049         if(this.rendered){
25050             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25051         }
25052     }
25053
25054 });
25055
25056 Roo.apply(Roo.bootstrap.CheckBox, {
25057     
25058     groups: {},
25059     
25060      /**
25061     * register a CheckBox Group
25062     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25063     */
25064     register : function(checkbox)
25065     {
25066         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25067             this.groups[checkbox.groupId] = {};
25068         }
25069         
25070         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25071             return;
25072         }
25073         
25074         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25075         
25076     },
25077     /**
25078     * fetch a CheckBox Group based on the group ID
25079     * @param {string} the group ID
25080     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25081     */
25082     get: function(groupId) {
25083         if (typeof(this.groups[groupId]) == 'undefined') {
25084             return false;
25085         }
25086         
25087         return this.groups[groupId] ;
25088     }
25089     
25090     
25091 });
25092 /*
25093  * - LGPL
25094  *
25095  * RadioItem
25096  * 
25097  */
25098
25099 /**
25100  * @class Roo.bootstrap.Radio
25101  * @extends Roo.bootstrap.Component
25102  * Bootstrap Radio class
25103  * @cfg {String} boxLabel - the label associated
25104  * @cfg {String} value - the value of radio
25105  * 
25106  * @constructor
25107  * Create a new Radio
25108  * @param {Object} config The config object
25109  */
25110 Roo.bootstrap.Radio = function(config){
25111     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25112     
25113 };
25114
25115 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25116     
25117     boxLabel : '',
25118     
25119     value : '',
25120     
25121     getAutoCreate : function()
25122     {
25123         var cfg = {
25124             tag : 'div',
25125             cls : 'form-group radio',
25126             cn : [
25127                 {
25128                     tag : 'label',
25129                     cls : 'box-label',
25130                     html : this.boxLabel
25131                 }
25132             ]
25133         };
25134         
25135         return cfg;
25136     },
25137     
25138     initEvents : function() 
25139     {
25140         this.parent().register(this);
25141         
25142         this.el.on('click', this.onClick, this);
25143         
25144     },
25145     
25146     onClick : function(e)
25147     {
25148         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25149             this.setChecked(true);
25150         }
25151     },
25152     
25153     setChecked : function(state, suppressEvent)
25154     {
25155         this.parent().setValue(this.value, suppressEvent);
25156         
25157     },
25158     
25159     setBoxLabel : function(v)
25160     {
25161         this.boxLabel = v;
25162         
25163         if(this.rendered){
25164             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25165         }
25166     }
25167     
25168 });
25169  
25170
25171  /*
25172  * - LGPL
25173  *
25174  * Input
25175  * 
25176  */
25177
25178 /**
25179  * @class Roo.bootstrap.SecurePass
25180  * @extends Roo.bootstrap.Input
25181  * Bootstrap SecurePass class
25182  *
25183  * 
25184  * @constructor
25185  * Create a new SecurePass
25186  * @param {Object} config The config object
25187  */
25188  
25189 Roo.bootstrap.SecurePass = function (config) {
25190     // these go here, so the translation tool can replace them..
25191     this.errors = {
25192         PwdEmpty: "Please type a password, and then retype it to confirm.",
25193         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25194         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25195         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25196         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25197         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25198         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25199         TooWeak: "Your password is Too Weak."
25200     },
25201     this.meterLabel = "Password strength:";
25202     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25203     this.meterClass = [
25204         "roo-password-meter-tooweak", 
25205         "roo-password-meter-weak", 
25206         "roo-password-meter-medium", 
25207         "roo-password-meter-strong", 
25208         "roo-password-meter-grey"
25209     ];
25210     
25211     this.errors = {};
25212     
25213     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25214 }
25215
25216 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25217     /**
25218      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25219      * {
25220      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25221      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25222      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25223      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25224      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25225      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25226      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25227      * })
25228      */
25229     // private
25230     
25231     meterWidth: 300,
25232     errorMsg :'',    
25233     errors: false,
25234     imageRoot: '/',
25235     /**
25236      * @cfg {String/Object} Label for the strength meter (defaults to
25237      * 'Password strength:')
25238      */
25239     // private
25240     meterLabel: '',
25241     /**
25242      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25243      * ['Weak', 'Medium', 'Strong'])
25244      */
25245     // private    
25246     pwdStrengths: false,    
25247     // private
25248     strength: 0,
25249     // private
25250     _lastPwd: null,
25251     // private
25252     kCapitalLetter: 0,
25253     kSmallLetter: 1,
25254     kDigit: 2,
25255     kPunctuation: 3,
25256     
25257     insecure: false,
25258     // private
25259     initEvents: function ()
25260     {
25261         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25262
25263         if (this.el.is('input[type=password]') && Roo.isSafari) {
25264             this.el.on('keydown', this.SafariOnKeyDown, this);
25265         }
25266
25267         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25268     },
25269     // private
25270     onRender: function (ct, position)
25271     {
25272         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25273         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25274         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25275
25276         this.trigger.createChild({
25277                    cn: [
25278                     {
25279                     //id: 'PwdMeter',
25280                     tag: 'div',
25281                     cls: 'roo-password-meter-grey col-xs-12',
25282                     style: {
25283                         //width: 0,
25284                         //width: this.meterWidth + 'px'                                                
25285                         }
25286                     },
25287                     {                            
25288                          cls: 'roo-password-meter-text'                          
25289                     }
25290                 ]            
25291         });
25292
25293          
25294         if (this.hideTrigger) {
25295             this.trigger.setDisplayed(false);
25296         }
25297         this.setSize(this.width || '', this.height || '');
25298     },
25299     // private
25300     onDestroy: function ()
25301     {
25302         if (this.trigger) {
25303             this.trigger.removeAllListeners();
25304             this.trigger.remove();
25305         }
25306         if (this.wrap) {
25307             this.wrap.remove();
25308         }
25309         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25310     },
25311     // private
25312     checkStrength: function ()
25313     {
25314         var pwd = this.inputEl().getValue();
25315         if (pwd == this._lastPwd) {
25316             return;
25317         }
25318
25319         var strength;
25320         if (this.ClientSideStrongPassword(pwd)) {
25321             strength = 3;
25322         } else if (this.ClientSideMediumPassword(pwd)) {
25323             strength = 2;
25324         } else if (this.ClientSideWeakPassword(pwd)) {
25325             strength = 1;
25326         } else {
25327             strength = 0;
25328         }
25329         
25330         Roo.log('strength1: ' + strength);
25331         
25332         //var pm = this.trigger.child('div/div/div').dom;
25333         var pm = this.trigger.child('div/div');
25334         pm.removeClass(this.meterClass);
25335         pm.addClass(this.meterClass[strength]);
25336                 
25337         
25338         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25339                 
25340         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25341         
25342         this._lastPwd = pwd;
25343     },
25344     reset: function ()
25345     {
25346         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25347         
25348         this._lastPwd = '';
25349         
25350         var pm = this.trigger.child('div/div');
25351         pm.removeClass(this.meterClass);
25352         pm.addClass('roo-password-meter-grey');        
25353         
25354         
25355         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25356         
25357         pt.innerHTML = '';
25358         this.inputEl().dom.type='password';
25359     },
25360     // private
25361     validateValue: function (value)
25362     {
25363         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25364             return false;
25365         }
25366         if (value.length == 0) {
25367             if (this.allowBlank) {
25368                 this.clearInvalid();
25369                 return true;
25370             }
25371
25372             this.markInvalid(this.errors.PwdEmpty);
25373             this.errorMsg = this.errors.PwdEmpty;
25374             return false;
25375         }
25376         
25377         if(this.insecure){
25378             return true;
25379         }
25380         
25381         if (!value.match(/[\x21-\x7e]+/)) {
25382             this.markInvalid(this.errors.PwdBadChar);
25383             this.errorMsg = this.errors.PwdBadChar;
25384             return false;
25385         }
25386         if (value.length < 6) {
25387             this.markInvalid(this.errors.PwdShort);
25388             this.errorMsg = this.errors.PwdShort;
25389             return false;
25390         }
25391         if (value.length > 16) {
25392             this.markInvalid(this.errors.PwdLong);
25393             this.errorMsg = this.errors.PwdLong;
25394             return false;
25395         }
25396         var strength;
25397         if (this.ClientSideStrongPassword(value)) {
25398             strength = 3;
25399         } else if (this.ClientSideMediumPassword(value)) {
25400             strength = 2;
25401         } else if (this.ClientSideWeakPassword(value)) {
25402             strength = 1;
25403         } else {
25404             strength = 0;
25405         }
25406
25407         
25408         if (strength < 2) {
25409             //this.markInvalid(this.errors.TooWeak);
25410             this.errorMsg = this.errors.TooWeak;
25411             //return false;
25412         }
25413         
25414         
25415         console.log('strength2: ' + strength);
25416         
25417         //var pm = this.trigger.child('div/div/div').dom;
25418         
25419         var pm = this.trigger.child('div/div');
25420         pm.removeClass(this.meterClass);
25421         pm.addClass(this.meterClass[strength]);
25422                 
25423         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25424                 
25425         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25426         
25427         this.errorMsg = ''; 
25428         return true;
25429     },
25430     // private
25431     CharacterSetChecks: function (type)
25432     {
25433         this.type = type;
25434         this.fResult = false;
25435     },
25436     // private
25437     isctype: function (character, type)
25438     {
25439         switch (type) {  
25440             case this.kCapitalLetter:
25441                 if (character >= 'A' && character <= 'Z') {
25442                     return true;
25443                 }
25444                 break;
25445             
25446             case this.kSmallLetter:
25447                 if (character >= 'a' && character <= 'z') {
25448                     return true;
25449                 }
25450                 break;
25451             
25452             case this.kDigit:
25453                 if (character >= '0' && character <= '9') {
25454                     return true;
25455                 }
25456                 break;
25457             
25458             case this.kPunctuation:
25459                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25460                     return true;
25461                 }
25462                 break;
25463             
25464             default:
25465                 return false;
25466         }
25467
25468     },
25469     // private
25470     IsLongEnough: function (pwd, size)
25471     {
25472         return !(pwd == null || isNaN(size) || pwd.length < size);
25473     },
25474     // private
25475     SpansEnoughCharacterSets: function (word, nb)
25476     {
25477         if (!this.IsLongEnough(word, nb))
25478         {
25479             return false;
25480         }
25481
25482         var characterSetChecks = new Array(
25483             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25484             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25485         );
25486         
25487         for (var index = 0; index < word.length; ++index) {
25488             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25489                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25490                     characterSetChecks[nCharSet].fResult = true;
25491                     break;
25492                 }
25493             }
25494         }
25495
25496         var nCharSets = 0;
25497         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25498             if (characterSetChecks[nCharSet].fResult) {
25499                 ++nCharSets;
25500             }
25501         }
25502
25503         if (nCharSets < nb) {
25504             return false;
25505         }
25506         return true;
25507     },
25508     // private
25509     ClientSideStrongPassword: function (pwd)
25510     {
25511         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25512     },
25513     // private
25514     ClientSideMediumPassword: function (pwd)
25515     {
25516         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25517     },
25518     // private
25519     ClientSideWeakPassword: function (pwd)
25520     {
25521         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25522     }
25523           
25524 })//<script type="text/javascript">
25525
25526 /*
25527  * Based  Ext JS Library 1.1.1
25528  * Copyright(c) 2006-2007, Ext JS, LLC.
25529  * LGPL
25530  *
25531  */
25532  
25533 /**
25534  * @class Roo.HtmlEditorCore
25535  * @extends Roo.Component
25536  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25537  *
25538  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25539  */
25540
25541 Roo.HtmlEditorCore = function(config){
25542     
25543     
25544     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25545     
25546     
25547     this.addEvents({
25548         /**
25549          * @event initialize
25550          * Fires when the editor is fully initialized (including the iframe)
25551          * @param {Roo.HtmlEditorCore} this
25552          */
25553         initialize: true,
25554         /**
25555          * @event activate
25556          * Fires when the editor is first receives the focus. Any insertion must wait
25557          * until after this event.
25558          * @param {Roo.HtmlEditorCore} this
25559          */
25560         activate: true,
25561          /**
25562          * @event beforesync
25563          * Fires before the textarea is updated with content from the editor iframe. Return false
25564          * to cancel the sync.
25565          * @param {Roo.HtmlEditorCore} this
25566          * @param {String} html
25567          */
25568         beforesync: true,
25569          /**
25570          * @event beforepush
25571          * Fires before the iframe editor is updated with content from the textarea. Return false
25572          * to cancel the push.
25573          * @param {Roo.HtmlEditorCore} this
25574          * @param {String} html
25575          */
25576         beforepush: true,
25577          /**
25578          * @event sync
25579          * Fires when the textarea is updated with content from the editor iframe.
25580          * @param {Roo.HtmlEditorCore} this
25581          * @param {String} html
25582          */
25583         sync: true,
25584          /**
25585          * @event push
25586          * Fires when the iframe editor is updated with content from the textarea.
25587          * @param {Roo.HtmlEditorCore} this
25588          * @param {String} html
25589          */
25590         push: true,
25591         
25592         /**
25593          * @event editorevent
25594          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25595          * @param {Roo.HtmlEditorCore} this
25596          */
25597         editorevent: true
25598         
25599     });
25600     
25601     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25602     
25603     // defaults : white / black...
25604     this.applyBlacklists();
25605     
25606     
25607     
25608 };
25609
25610
25611 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25612
25613
25614      /**
25615      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25616      */
25617     
25618     owner : false,
25619     
25620      /**
25621      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25622      *                        Roo.resizable.
25623      */
25624     resizable : false,
25625      /**
25626      * @cfg {Number} height (in pixels)
25627      */   
25628     height: 300,
25629    /**
25630      * @cfg {Number} width (in pixels)
25631      */   
25632     width: 500,
25633     
25634     /**
25635      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25636      * 
25637      */
25638     stylesheets: false,
25639     
25640     /**
25641      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25642      */
25643     allowComments: false,
25644     // id of frame..
25645     frameId: false,
25646     
25647     // private properties
25648     validationEvent : false,
25649     deferHeight: true,
25650     initialized : false,
25651     activated : false,
25652     sourceEditMode : false,
25653     onFocus : Roo.emptyFn,
25654     iframePad:3,
25655     hideMode:'offsets',
25656     
25657     clearUp: true,
25658     
25659     // blacklist + whitelisted elements..
25660     black: false,
25661     white: false,
25662      
25663     bodyCls : '',
25664
25665     /**
25666      * Protected method that will not generally be called directly. It
25667      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25668      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25669      */
25670     getDocMarkup : function(){
25671         // body styles..
25672         var st = '';
25673         
25674         // inherit styels from page...?? 
25675         if (this.stylesheets === false) {
25676             
25677             Roo.get(document.head).select('style').each(function(node) {
25678                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25679             });
25680             
25681             Roo.get(document.head).select('link').each(function(node) { 
25682                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25683             });
25684             
25685         } else if (!this.stylesheets.length) {
25686                 // simple..
25687                 st = '<style type="text/css">' +
25688                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25689                    '</style>';
25690         } else {
25691             for (var i in this.stylesheets) { 
25692                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25693             }
25694             
25695         }
25696         
25697         st +=  '<style type="text/css">' +
25698             'IMG { cursor: pointer } ' +
25699         '</style>';
25700
25701         var cls = 'roo-htmleditor-body';
25702         
25703         if(this.bodyCls.length){
25704             cls += ' ' + this.bodyCls;
25705         }
25706         
25707         return '<html><head>' + st  +
25708             //<style type="text/css">' +
25709             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25710             //'</style>' +
25711             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25712     },
25713
25714     // private
25715     onRender : function(ct, position)
25716     {
25717         var _t = this;
25718         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25719         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25720         
25721         
25722         this.el.dom.style.border = '0 none';
25723         this.el.dom.setAttribute('tabIndex', -1);
25724         this.el.addClass('x-hidden hide');
25725         
25726         
25727         
25728         if(Roo.isIE){ // fix IE 1px bogus margin
25729             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25730         }
25731        
25732         
25733         this.frameId = Roo.id();
25734         
25735          
25736         
25737         var iframe = this.owner.wrap.createChild({
25738             tag: 'iframe',
25739             cls: 'form-control', // bootstrap..
25740             id: this.frameId,
25741             name: this.frameId,
25742             frameBorder : 'no',
25743             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25744         }, this.el
25745         );
25746         
25747         
25748         this.iframe = iframe.dom;
25749
25750          this.assignDocWin();
25751         
25752         this.doc.designMode = 'on';
25753        
25754         this.doc.open();
25755         this.doc.write(this.getDocMarkup());
25756         this.doc.close();
25757
25758         
25759         var task = { // must defer to wait for browser to be ready
25760             run : function(){
25761                 //console.log("run task?" + this.doc.readyState);
25762                 this.assignDocWin();
25763                 if(this.doc.body || this.doc.readyState == 'complete'){
25764                     try {
25765                         this.doc.designMode="on";
25766                     } catch (e) {
25767                         return;
25768                     }
25769                     Roo.TaskMgr.stop(task);
25770                     this.initEditor.defer(10, this);
25771                 }
25772             },
25773             interval : 10,
25774             duration: 10000,
25775             scope: this
25776         };
25777         Roo.TaskMgr.start(task);
25778
25779     },
25780
25781     // private
25782     onResize : function(w, h)
25783     {
25784          Roo.log('resize: ' +w + ',' + h );
25785         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25786         if(!this.iframe){
25787             return;
25788         }
25789         if(typeof w == 'number'){
25790             
25791             this.iframe.style.width = w + 'px';
25792         }
25793         if(typeof h == 'number'){
25794             
25795             this.iframe.style.height = h + 'px';
25796             if(this.doc){
25797                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25798             }
25799         }
25800         
25801     },
25802
25803     /**
25804      * Toggles the editor between standard and source edit mode.
25805      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25806      */
25807     toggleSourceEdit : function(sourceEditMode){
25808         
25809         this.sourceEditMode = sourceEditMode === true;
25810         
25811         if(this.sourceEditMode){
25812  
25813             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25814             
25815         }else{
25816             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25817             //this.iframe.className = '';
25818             this.deferFocus();
25819         }
25820         //this.setSize(this.owner.wrap.getSize());
25821         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25822     },
25823
25824     
25825   
25826
25827     /**
25828      * Protected method that will not generally be called directly. If you need/want
25829      * custom HTML cleanup, this is the method you should override.
25830      * @param {String} html The HTML to be cleaned
25831      * return {String} The cleaned HTML
25832      */
25833     cleanHtml : function(html){
25834         html = String(html);
25835         if(html.length > 5){
25836             if(Roo.isSafari){ // strip safari nonsense
25837                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25838             }
25839         }
25840         if(html == '&nbsp;'){
25841             html = '';
25842         }
25843         return html;
25844     },
25845
25846     /**
25847      * HTML Editor -> Textarea
25848      * Protected method that will not generally be called directly. Syncs the contents
25849      * of the editor iframe with the textarea.
25850      */
25851     syncValue : function(){
25852         if(this.initialized){
25853             var bd = (this.doc.body || this.doc.documentElement);
25854             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25855             var html = bd.innerHTML;
25856             if(Roo.isSafari){
25857                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25858                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25859                 if(m && m[1]){
25860                     html = '<div style="'+m[0]+'">' + html + '</div>';
25861                 }
25862             }
25863             html = this.cleanHtml(html);
25864             // fix up the special chars.. normaly like back quotes in word...
25865             // however we do not want to do this with chinese..
25866             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25867                 
25868                 var cc = match.charCodeAt();
25869
25870                 // Get the character value, handling surrogate pairs
25871                 if (match.length == 2) {
25872                     // It's a surrogate pair, calculate the Unicode code point
25873                     var high = match.charCodeAt(0) - 0xD800;
25874                     var low  = match.charCodeAt(1) - 0xDC00;
25875                     cc = (high * 0x400) + low + 0x10000;
25876                 }  else if (
25877                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25878                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25879                     (cc >= 0xf900 && cc < 0xfb00 )
25880                 ) {
25881                         return match;
25882                 }  
25883          
25884                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25885                 return "&#" + cc + ";";
25886                 
25887                 
25888             });
25889             
25890             
25891              
25892             if(this.owner.fireEvent('beforesync', this, html) !== false){
25893                 this.el.dom.value = html;
25894                 this.owner.fireEvent('sync', this, html);
25895             }
25896         }
25897     },
25898
25899     /**
25900      * Protected method that will not generally be called directly. Pushes the value of the textarea
25901      * into the iframe editor.
25902      */
25903     pushValue : function(){
25904         if(this.initialized){
25905             var v = this.el.dom.value.trim();
25906             
25907 //            if(v.length < 1){
25908 //                v = '&#160;';
25909 //            }
25910             
25911             if(this.owner.fireEvent('beforepush', this, v) !== false){
25912                 var d = (this.doc.body || this.doc.documentElement);
25913                 d.innerHTML = v;
25914                 this.cleanUpPaste();
25915                 this.el.dom.value = d.innerHTML;
25916                 this.owner.fireEvent('push', this, v);
25917             }
25918         }
25919     },
25920
25921     // private
25922     deferFocus : function(){
25923         this.focus.defer(10, this);
25924     },
25925
25926     // doc'ed in Field
25927     focus : function(){
25928         if(this.win && !this.sourceEditMode){
25929             this.win.focus();
25930         }else{
25931             this.el.focus();
25932         }
25933     },
25934     
25935     assignDocWin: function()
25936     {
25937         var iframe = this.iframe;
25938         
25939          if(Roo.isIE){
25940             this.doc = iframe.contentWindow.document;
25941             this.win = iframe.contentWindow;
25942         } else {
25943 //            if (!Roo.get(this.frameId)) {
25944 //                return;
25945 //            }
25946 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25947 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25948             
25949             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25950                 return;
25951             }
25952             
25953             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25954             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25955         }
25956     },
25957     
25958     // private
25959     initEditor : function(){
25960         //console.log("INIT EDITOR");
25961         this.assignDocWin();
25962         
25963         
25964         
25965         this.doc.designMode="on";
25966         this.doc.open();
25967         this.doc.write(this.getDocMarkup());
25968         this.doc.close();
25969         
25970         var dbody = (this.doc.body || this.doc.documentElement);
25971         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25972         // this copies styles from the containing element into thsi one..
25973         // not sure why we need all of this..
25974         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25975         
25976         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25977         //ss['background-attachment'] = 'fixed'; // w3c
25978         dbody.bgProperties = 'fixed'; // ie
25979         //Roo.DomHelper.applyStyles(dbody, ss);
25980         Roo.EventManager.on(this.doc, {
25981             //'mousedown': this.onEditorEvent,
25982             'mouseup': this.onEditorEvent,
25983             'dblclick': this.onEditorEvent,
25984             'click': this.onEditorEvent,
25985             'keyup': this.onEditorEvent,
25986             buffer:100,
25987             scope: this
25988         });
25989         if(Roo.isGecko){
25990             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25991         }
25992         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25993             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25994         }
25995         this.initialized = true;
25996
25997         this.owner.fireEvent('initialize', this);
25998         this.pushValue();
25999     },
26000
26001     // private
26002     onDestroy : function(){
26003         
26004         
26005         
26006         if(this.rendered){
26007             
26008             //for (var i =0; i < this.toolbars.length;i++) {
26009             //    // fixme - ask toolbars for heights?
26010             //    this.toolbars[i].onDestroy();
26011            // }
26012             
26013             //this.wrap.dom.innerHTML = '';
26014             //this.wrap.remove();
26015         }
26016     },
26017
26018     // private
26019     onFirstFocus : function(){
26020         
26021         this.assignDocWin();
26022         
26023         
26024         this.activated = true;
26025          
26026     
26027         if(Roo.isGecko){ // prevent silly gecko errors
26028             this.win.focus();
26029             var s = this.win.getSelection();
26030             if(!s.focusNode || s.focusNode.nodeType != 3){
26031                 var r = s.getRangeAt(0);
26032                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26033                 r.collapse(true);
26034                 this.deferFocus();
26035             }
26036             try{
26037                 this.execCmd('useCSS', true);
26038                 this.execCmd('styleWithCSS', false);
26039             }catch(e){}
26040         }
26041         this.owner.fireEvent('activate', this);
26042     },
26043
26044     // private
26045     adjustFont: function(btn){
26046         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26047         //if(Roo.isSafari){ // safari
26048         //    adjust *= 2;
26049        // }
26050         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26051         if(Roo.isSafari){ // safari
26052             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26053             v =  (v < 10) ? 10 : v;
26054             v =  (v > 48) ? 48 : v;
26055             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26056             
26057         }
26058         
26059         
26060         v = Math.max(1, v+adjust);
26061         
26062         this.execCmd('FontSize', v  );
26063     },
26064
26065     onEditorEvent : function(e)
26066     {
26067         this.owner.fireEvent('editorevent', this, e);
26068       //  this.updateToolbar();
26069         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26070     },
26071
26072     insertTag : function(tg)
26073     {
26074         // could be a bit smarter... -> wrap the current selected tRoo..
26075         if (tg.toLowerCase() == 'span' ||
26076             tg.toLowerCase() == 'code' ||
26077             tg.toLowerCase() == 'sup' ||
26078             tg.toLowerCase() == 'sub' 
26079             ) {
26080             
26081             range = this.createRange(this.getSelection());
26082             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26083             wrappingNode.appendChild(range.extractContents());
26084             range.insertNode(wrappingNode);
26085
26086             return;
26087             
26088             
26089             
26090         }
26091         this.execCmd("formatblock",   tg);
26092         
26093     },
26094     
26095     insertText : function(txt)
26096     {
26097         
26098         
26099         var range = this.createRange();
26100         range.deleteContents();
26101                //alert(Sender.getAttribute('label'));
26102                
26103         range.insertNode(this.doc.createTextNode(txt));
26104     } ,
26105     
26106      
26107
26108     /**
26109      * Executes a Midas editor command on the editor document and performs necessary focus and
26110      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26111      * @param {String} cmd The Midas command
26112      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26113      */
26114     relayCmd : function(cmd, value){
26115         this.win.focus();
26116         this.execCmd(cmd, value);
26117         this.owner.fireEvent('editorevent', this);
26118         //this.updateToolbar();
26119         this.owner.deferFocus();
26120     },
26121
26122     /**
26123      * Executes a Midas editor command directly on the editor document.
26124      * For visual commands, you should use {@link #relayCmd} instead.
26125      * <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     execCmd : function(cmd, value){
26130         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26131         this.syncValue();
26132     },
26133  
26134  
26135    
26136     /**
26137      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26138      * to insert tRoo.
26139      * @param {String} text | dom node.. 
26140      */
26141     insertAtCursor : function(text)
26142     {
26143         
26144         if(!this.activated){
26145             return;
26146         }
26147         /*
26148         if(Roo.isIE){
26149             this.win.focus();
26150             var r = this.doc.selection.createRange();
26151             if(r){
26152                 r.collapse(true);
26153                 r.pasteHTML(text);
26154                 this.syncValue();
26155                 this.deferFocus();
26156             
26157             }
26158             return;
26159         }
26160         */
26161         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26162             this.win.focus();
26163             
26164             
26165             // from jquery ui (MIT licenced)
26166             var range, node;
26167             var win = this.win;
26168             
26169             if (win.getSelection && win.getSelection().getRangeAt) {
26170                 range = win.getSelection().getRangeAt(0);
26171                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26172                 range.insertNode(node);
26173             } else if (win.document.selection && win.document.selection.createRange) {
26174                 // no firefox support
26175                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26176                 win.document.selection.createRange().pasteHTML(txt);
26177             } else {
26178                 // no firefox support
26179                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26180                 this.execCmd('InsertHTML', txt);
26181             } 
26182             
26183             this.syncValue();
26184             
26185             this.deferFocus();
26186         }
26187     },
26188  // private
26189     mozKeyPress : function(e){
26190         if(e.ctrlKey){
26191             var c = e.getCharCode(), cmd;
26192           
26193             if(c > 0){
26194                 c = String.fromCharCode(c).toLowerCase();
26195                 switch(c){
26196                     case 'b':
26197                         cmd = 'bold';
26198                         break;
26199                     case 'i':
26200                         cmd = 'italic';
26201                         break;
26202                     
26203                     case 'u':
26204                         cmd = 'underline';
26205                         break;
26206                     
26207                     case 'v':
26208                         this.cleanUpPaste.defer(100, this);
26209                         return;
26210                         
26211                 }
26212                 if(cmd){
26213                     this.win.focus();
26214                     this.execCmd(cmd);
26215                     this.deferFocus();
26216                     e.preventDefault();
26217                 }
26218                 
26219             }
26220         }
26221     },
26222
26223     // private
26224     fixKeys : function(){ // load time branching for fastest keydown performance
26225         if(Roo.isIE){
26226             return function(e){
26227                 var k = e.getKey(), r;
26228                 if(k == e.TAB){
26229                     e.stopEvent();
26230                     r = this.doc.selection.createRange();
26231                     if(r){
26232                         r.collapse(true);
26233                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26234                         this.deferFocus();
26235                     }
26236                     return;
26237                 }
26238                 
26239                 if(k == e.ENTER){
26240                     r = this.doc.selection.createRange();
26241                     if(r){
26242                         var target = r.parentElement();
26243                         if(!target || target.tagName.toLowerCase() != 'li'){
26244                             e.stopEvent();
26245                             r.pasteHTML('<br />');
26246                             r.collapse(false);
26247                             r.select();
26248                         }
26249                     }
26250                 }
26251                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26252                     this.cleanUpPaste.defer(100, this);
26253                     return;
26254                 }
26255                 
26256                 
26257             };
26258         }else if(Roo.isOpera){
26259             return function(e){
26260                 var k = e.getKey();
26261                 if(k == e.TAB){
26262                     e.stopEvent();
26263                     this.win.focus();
26264                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26265                     this.deferFocus();
26266                 }
26267                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26268                     this.cleanUpPaste.defer(100, this);
26269                     return;
26270                 }
26271                 
26272             };
26273         }else if(Roo.isSafari){
26274             return function(e){
26275                 var k = e.getKey();
26276                 
26277                 if(k == e.TAB){
26278                     e.stopEvent();
26279                     this.execCmd('InsertText','\t');
26280                     this.deferFocus();
26281                     return;
26282                 }
26283                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26284                     this.cleanUpPaste.defer(100, this);
26285                     return;
26286                 }
26287                 
26288              };
26289         }
26290     }(),
26291     
26292     getAllAncestors: function()
26293     {
26294         var p = this.getSelectedNode();
26295         var a = [];
26296         if (!p) {
26297             a.push(p); // push blank onto stack..
26298             p = this.getParentElement();
26299         }
26300         
26301         
26302         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26303             a.push(p);
26304             p = p.parentNode;
26305         }
26306         a.push(this.doc.body);
26307         return a;
26308     },
26309     lastSel : false,
26310     lastSelNode : false,
26311     
26312     
26313     getSelection : function() 
26314     {
26315         this.assignDocWin();
26316         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26317     },
26318     
26319     getSelectedNode: function() 
26320     {
26321         // this may only work on Gecko!!!
26322         
26323         // should we cache this!!!!
26324         
26325         
26326         
26327          
26328         var range = this.createRange(this.getSelection()).cloneRange();
26329         
26330         if (Roo.isIE) {
26331             var parent = range.parentElement();
26332             while (true) {
26333                 var testRange = range.duplicate();
26334                 testRange.moveToElementText(parent);
26335                 if (testRange.inRange(range)) {
26336                     break;
26337                 }
26338                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26339                     break;
26340                 }
26341                 parent = parent.parentElement;
26342             }
26343             return parent;
26344         }
26345         
26346         // is ancestor a text element.
26347         var ac =  range.commonAncestorContainer;
26348         if (ac.nodeType == 3) {
26349             ac = ac.parentNode;
26350         }
26351         
26352         var ar = ac.childNodes;
26353          
26354         var nodes = [];
26355         var other_nodes = [];
26356         var has_other_nodes = false;
26357         for (var i=0;i<ar.length;i++) {
26358             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26359                 continue;
26360             }
26361             // fullly contained node.
26362             
26363             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26364                 nodes.push(ar[i]);
26365                 continue;
26366             }
26367             
26368             // probably selected..
26369             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26370                 other_nodes.push(ar[i]);
26371                 continue;
26372             }
26373             // outer..
26374             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26375                 continue;
26376             }
26377             
26378             
26379             has_other_nodes = true;
26380         }
26381         if (!nodes.length && other_nodes.length) {
26382             nodes= other_nodes;
26383         }
26384         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26385             return false;
26386         }
26387         
26388         return nodes[0];
26389     },
26390     createRange: function(sel)
26391     {
26392         // this has strange effects when using with 
26393         // top toolbar - not sure if it's a great idea.
26394         //this.editor.contentWindow.focus();
26395         if (typeof sel != "undefined") {
26396             try {
26397                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26398             } catch(e) {
26399                 return this.doc.createRange();
26400             }
26401         } else {
26402             return this.doc.createRange();
26403         }
26404     },
26405     getParentElement: function()
26406     {
26407         
26408         this.assignDocWin();
26409         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26410         
26411         var range = this.createRange(sel);
26412          
26413         try {
26414             var p = range.commonAncestorContainer;
26415             while (p.nodeType == 3) { // text node
26416                 p = p.parentNode;
26417             }
26418             return p;
26419         } catch (e) {
26420             return null;
26421         }
26422     
26423     },
26424     /***
26425      *
26426      * Range intersection.. the hard stuff...
26427      *  '-1' = before
26428      *  '0' = hits..
26429      *  '1' = after.
26430      *         [ -- selected range --- ]
26431      *   [fail]                        [fail]
26432      *
26433      *    basically..
26434      *      if end is before start or  hits it. fail.
26435      *      if start is after end or hits it fail.
26436      *
26437      *   if either hits (but other is outside. - then it's not 
26438      *   
26439      *    
26440      **/
26441     
26442     
26443     // @see http://www.thismuchiknow.co.uk/?p=64.
26444     rangeIntersectsNode : function(range, node)
26445     {
26446         var nodeRange = node.ownerDocument.createRange();
26447         try {
26448             nodeRange.selectNode(node);
26449         } catch (e) {
26450             nodeRange.selectNodeContents(node);
26451         }
26452     
26453         var rangeStartRange = range.cloneRange();
26454         rangeStartRange.collapse(true);
26455     
26456         var rangeEndRange = range.cloneRange();
26457         rangeEndRange.collapse(false);
26458     
26459         var nodeStartRange = nodeRange.cloneRange();
26460         nodeStartRange.collapse(true);
26461     
26462         var nodeEndRange = nodeRange.cloneRange();
26463         nodeEndRange.collapse(false);
26464     
26465         return rangeStartRange.compareBoundaryPoints(
26466                  Range.START_TO_START, nodeEndRange) == -1 &&
26467                rangeEndRange.compareBoundaryPoints(
26468                  Range.START_TO_START, nodeStartRange) == 1;
26469         
26470          
26471     },
26472     rangeCompareNode : function(range, node)
26473     {
26474         var nodeRange = node.ownerDocument.createRange();
26475         try {
26476             nodeRange.selectNode(node);
26477         } catch (e) {
26478             nodeRange.selectNodeContents(node);
26479         }
26480         
26481         
26482         range.collapse(true);
26483     
26484         nodeRange.collapse(true);
26485      
26486         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26487         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26488          
26489         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26490         
26491         var nodeIsBefore   =  ss == 1;
26492         var nodeIsAfter    = ee == -1;
26493         
26494         if (nodeIsBefore && nodeIsAfter) {
26495             return 0; // outer
26496         }
26497         if (!nodeIsBefore && nodeIsAfter) {
26498             return 1; //right trailed.
26499         }
26500         
26501         if (nodeIsBefore && !nodeIsAfter) {
26502             return 2;  // left trailed.
26503         }
26504         // fully contined.
26505         return 3;
26506     },
26507
26508     // private? - in a new class?
26509     cleanUpPaste :  function()
26510     {
26511         // cleans up the whole document..
26512         Roo.log('cleanuppaste');
26513         
26514         this.cleanUpChildren(this.doc.body);
26515         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26516         if (clean != this.doc.body.innerHTML) {
26517             this.doc.body.innerHTML = clean;
26518         }
26519         
26520     },
26521     
26522     cleanWordChars : function(input) {// change the chars to hex code
26523         var he = Roo.HtmlEditorCore;
26524         
26525         var output = input;
26526         Roo.each(he.swapCodes, function(sw) { 
26527             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26528             
26529             output = output.replace(swapper, sw[1]);
26530         });
26531         
26532         return output;
26533     },
26534     
26535     
26536     cleanUpChildren : function (n)
26537     {
26538         if (!n.childNodes.length) {
26539             return;
26540         }
26541         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26542            this.cleanUpChild(n.childNodes[i]);
26543         }
26544     },
26545     
26546     
26547         
26548     
26549     cleanUpChild : function (node)
26550     {
26551         var ed = this;
26552         //console.log(node);
26553         if (node.nodeName == "#text") {
26554             // clean up silly Windows -- stuff?
26555             return; 
26556         }
26557         if (node.nodeName == "#comment") {
26558             if (!this.allowComments) {
26559                 node.parentNode.removeChild(node);
26560             }
26561             // clean up silly Windows -- stuff?
26562             return; 
26563         }
26564         var lcname = node.tagName.toLowerCase();
26565         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26566         // whitelist of tags..
26567         
26568         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26569             // remove node.
26570             node.parentNode.removeChild(node);
26571             return;
26572             
26573         }
26574         
26575         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26576         
26577         // spans with no attributes - just remove them..
26578         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26579             remove_keep_children = true;
26580         }
26581         
26582         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26583         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26584         
26585         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26586         //    remove_keep_children = true;
26587         //}
26588         
26589         if (remove_keep_children) {
26590             this.cleanUpChildren(node);
26591             // inserts everything just before this node...
26592             while (node.childNodes.length) {
26593                 var cn = node.childNodes[0];
26594                 node.removeChild(cn);
26595                 node.parentNode.insertBefore(cn, node);
26596             }
26597             node.parentNode.removeChild(node);
26598             return;
26599         }
26600         
26601         if (!node.attributes || !node.attributes.length) {
26602             
26603           
26604             
26605             
26606             this.cleanUpChildren(node);
26607             return;
26608         }
26609         
26610         function cleanAttr(n,v)
26611         {
26612             
26613             if (v.match(/^\./) || v.match(/^\//)) {
26614                 return;
26615             }
26616             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26617                 return;
26618             }
26619             if (v.match(/^#/)) {
26620                 return;
26621             }
26622             if (v.match(/^\{/)) { // allow template editing.
26623                 return;
26624             }
26625 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26626             node.removeAttribute(n);
26627             
26628         }
26629         
26630         var cwhite = this.cwhite;
26631         var cblack = this.cblack;
26632             
26633         function cleanStyle(n,v)
26634         {
26635             if (v.match(/expression/)) { //XSS?? should we even bother..
26636                 node.removeAttribute(n);
26637                 return;
26638             }
26639             
26640             var parts = v.split(/;/);
26641             var clean = [];
26642             
26643             Roo.each(parts, function(p) {
26644                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26645                 if (!p.length) {
26646                     return true;
26647                 }
26648                 var l = p.split(':').shift().replace(/\s+/g,'');
26649                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26650                 
26651                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26652 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26653                     //node.removeAttribute(n);
26654                     return true;
26655                 }
26656                 //Roo.log()
26657                 // only allow 'c whitelisted system attributes'
26658                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26659 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26660                     //node.removeAttribute(n);
26661                     return true;
26662                 }
26663                 
26664                 
26665                  
26666                 
26667                 clean.push(p);
26668                 return true;
26669             });
26670             if (clean.length) { 
26671                 node.setAttribute(n, clean.join(';'));
26672             } else {
26673                 node.removeAttribute(n);
26674             }
26675             
26676         }
26677         
26678         
26679         for (var i = node.attributes.length-1; i > -1 ; i--) {
26680             var a = node.attributes[i];
26681             //console.log(a);
26682             
26683             if (a.name.toLowerCase().substr(0,2)=='on')  {
26684                 node.removeAttribute(a.name);
26685                 continue;
26686             }
26687             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26688                 node.removeAttribute(a.name);
26689                 continue;
26690             }
26691             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26692                 cleanAttr(a.name,a.value); // fixme..
26693                 continue;
26694             }
26695             if (a.name == 'style') {
26696                 cleanStyle(a.name,a.value);
26697                 continue;
26698             }
26699             /// clean up MS crap..
26700             // tecnically this should be a list of valid class'es..
26701             
26702             
26703             if (a.name == 'class') {
26704                 if (a.value.match(/^Mso/)) {
26705                     node.removeAttribute('class');
26706                 }
26707                 
26708                 if (a.value.match(/^body$/)) {
26709                     node.removeAttribute('class');
26710                 }
26711                 continue;
26712             }
26713             
26714             // style cleanup!?
26715             // class cleanup?
26716             
26717         }
26718         
26719         
26720         this.cleanUpChildren(node);
26721         
26722         
26723     },
26724     
26725     /**
26726      * Clean up MS wordisms...
26727      */
26728     cleanWord : function(node)
26729     {
26730         if (!node) {
26731             this.cleanWord(this.doc.body);
26732             return;
26733         }
26734         
26735         if(
26736                 node.nodeName == 'SPAN' &&
26737                 !node.hasAttributes() &&
26738                 node.childNodes.length == 1 &&
26739                 node.firstChild.nodeName == "#text"  
26740         ) {
26741             var textNode = node.firstChild;
26742             node.removeChild(textNode);
26743             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26744                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26745             }
26746             node.parentNode.insertBefore(textNode, node);
26747             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26748                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26749             }
26750             node.parentNode.removeChild(node);
26751         }
26752         
26753         if (node.nodeName == "#text") {
26754             // clean up silly Windows -- stuff?
26755             return; 
26756         }
26757         if (node.nodeName == "#comment") {
26758             node.parentNode.removeChild(node);
26759             // clean up silly Windows -- stuff?
26760             return; 
26761         }
26762         
26763         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26764             node.parentNode.removeChild(node);
26765             return;
26766         }
26767         //Roo.log(node.tagName);
26768         // remove - but keep children..
26769         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26770             //Roo.log('-- removed');
26771             while (node.childNodes.length) {
26772                 var cn = node.childNodes[0];
26773                 node.removeChild(cn);
26774                 node.parentNode.insertBefore(cn, node);
26775                 // move node to parent - and clean it..
26776                 this.cleanWord(cn);
26777             }
26778             node.parentNode.removeChild(node);
26779             /// no need to iterate chidlren = it's got none..
26780             //this.iterateChildren(node, this.cleanWord);
26781             return;
26782         }
26783         // clean styles
26784         if (node.className.length) {
26785             
26786             var cn = node.className.split(/\W+/);
26787             var cna = [];
26788             Roo.each(cn, function(cls) {
26789                 if (cls.match(/Mso[a-zA-Z]+/)) {
26790                     return;
26791                 }
26792                 cna.push(cls);
26793             });
26794             node.className = cna.length ? cna.join(' ') : '';
26795             if (!cna.length) {
26796                 node.removeAttribute("class");
26797             }
26798         }
26799         
26800         if (node.hasAttribute("lang")) {
26801             node.removeAttribute("lang");
26802         }
26803         
26804         if (node.hasAttribute("style")) {
26805             
26806             var styles = node.getAttribute("style").split(";");
26807             var nstyle = [];
26808             Roo.each(styles, function(s) {
26809                 if (!s.match(/:/)) {
26810                     return;
26811                 }
26812                 var kv = s.split(":");
26813                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26814                     return;
26815                 }
26816                 // what ever is left... we allow.
26817                 nstyle.push(s);
26818             });
26819             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26820             if (!nstyle.length) {
26821                 node.removeAttribute('style');
26822             }
26823         }
26824         this.iterateChildren(node, this.cleanWord);
26825         
26826         
26827         
26828     },
26829     /**
26830      * iterateChildren of a Node, calling fn each time, using this as the scole..
26831      * @param {DomNode} node node to iterate children of.
26832      * @param {Function} fn method of this class to call on each item.
26833      */
26834     iterateChildren : function(node, fn)
26835     {
26836         if (!node.childNodes.length) {
26837                 return;
26838         }
26839         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26840            fn.call(this, node.childNodes[i])
26841         }
26842     },
26843     
26844     
26845     /**
26846      * cleanTableWidths.
26847      *
26848      * Quite often pasting from word etc.. results in tables with column and widths.
26849      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26850      *
26851      */
26852     cleanTableWidths : function(node)
26853     {
26854          
26855          
26856         if (!node) {
26857             this.cleanTableWidths(this.doc.body);
26858             return;
26859         }
26860         
26861         // ignore list...
26862         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26863             return; 
26864         }
26865         Roo.log(node.tagName);
26866         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26867             this.iterateChildren(node, this.cleanTableWidths);
26868             return;
26869         }
26870         if (node.hasAttribute('width')) {
26871             node.removeAttribute('width');
26872         }
26873         
26874          
26875         if (node.hasAttribute("style")) {
26876             // pretty basic...
26877             
26878             var styles = node.getAttribute("style").split(";");
26879             var nstyle = [];
26880             Roo.each(styles, function(s) {
26881                 if (!s.match(/:/)) {
26882                     return;
26883                 }
26884                 var kv = s.split(":");
26885                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26886                     return;
26887                 }
26888                 // what ever is left... we allow.
26889                 nstyle.push(s);
26890             });
26891             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26892             if (!nstyle.length) {
26893                 node.removeAttribute('style');
26894             }
26895         }
26896         
26897         this.iterateChildren(node, this.cleanTableWidths);
26898         
26899         
26900     },
26901     
26902     
26903     
26904     
26905     domToHTML : function(currentElement, depth, nopadtext) {
26906         
26907         depth = depth || 0;
26908         nopadtext = nopadtext || false;
26909     
26910         if (!currentElement) {
26911             return this.domToHTML(this.doc.body);
26912         }
26913         
26914         //Roo.log(currentElement);
26915         var j;
26916         var allText = false;
26917         var nodeName = currentElement.nodeName;
26918         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26919         
26920         if  (nodeName == '#text') {
26921             
26922             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26923         }
26924         
26925         
26926         var ret = '';
26927         if (nodeName != 'BODY') {
26928              
26929             var i = 0;
26930             // Prints the node tagName, such as <A>, <IMG>, etc
26931             if (tagName) {
26932                 var attr = [];
26933                 for(i = 0; i < currentElement.attributes.length;i++) {
26934                     // quoting?
26935                     var aname = currentElement.attributes.item(i).name;
26936                     if (!currentElement.attributes.item(i).value.length) {
26937                         continue;
26938                     }
26939                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26940                 }
26941                 
26942                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26943             } 
26944             else {
26945                 
26946                 // eack
26947             }
26948         } else {
26949             tagName = false;
26950         }
26951         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26952             return ret;
26953         }
26954         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26955             nopadtext = true;
26956         }
26957         
26958         
26959         // Traverse the tree
26960         i = 0;
26961         var currentElementChild = currentElement.childNodes.item(i);
26962         var allText = true;
26963         var innerHTML  = '';
26964         lastnode = '';
26965         while (currentElementChild) {
26966             // Formatting code (indent the tree so it looks nice on the screen)
26967             var nopad = nopadtext;
26968             if (lastnode == 'SPAN') {
26969                 nopad  = true;
26970             }
26971             // text
26972             if  (currentElementChild.nodeName == '#text') {
26973                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26974                 toadd = nopadtext ? toadd : toadd.trim();
26975                 if (!nopad && toadd.length > 80) {
26976                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26977                 }
26978                 innerHTML  += toadd;
26979                 
26980                 i++;
26981                 currentElementChild = currentElement.childNodes.item(i);
26982                 lastNode = '';
26983                 continue;
26984             }
26985             allText = false;
26986             
26987             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26988                 
26989             // Recursively traverse the tree structure of the child node
26990             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26991             lastnode = currentElementChild.nodeName;
26992             i++;
26993             currentElementChild=currentElement.childNodes.item(i);
26994         }
26995         
26996         ret += innerHTML;
26997         
26998         if (!allText) {
26999                 // The remaining code is mostly for formatting the tree
27000             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27001         }
27002         
27003         
27004         if (tagName) {
27005             ret+= "</"+tagName+">";
27006         }
27007         return ret;
27008         
27009     },
27010         
27011     applyBlacklists : function()
27012     {
27013         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27014         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27015         
27016         this.white = [];
27017         this.black = [];
27018         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27019             if (b.indexOf(tag) > -1) {
27020                 return;
27021             }
27022             this.white.push(tag);
27023             
27024         }, this);
27025         
27026         Roo.each(w, function(tag) {
27027             if (b.indexOf(tag) > -1) {
27028                 return;
27029             }
27030             if (this.white.indexOf(tag) > -1) {
27031                 return;
27032             }
27033             this.white.push(tag);
27034             
27035         }, this);
27036         
27037         
27038         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27039             if (w.indexOf(tag) > -1) {
27040                 return;
27041             }
27042             this.black.push(tag);
27043             
27044         }, this);
27045         
27046         Roo.each(b, function(tag) {
27047             if (w.indexOf(tag) > -1) {
27048                 return;
27049             }
27050             if (this.black.indexOf(tag) > -1) {
27051                 return;
27052             }
27053             this.black.push(tag);
27054             
27055         }, this);
27056         
27057         
27058         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27059         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27060         
27061         this.cwhite = [];
27062         this.cblack = [];
27063         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27064             if (b.indexOf(tag) > -1) {
27065                 return;
27066             }
27067             this.cwhite.push(tag);
27068             
27069         }, this);
27070         
27071         Roo.each(w, function(tag) {
27072             if (b.indexOf(tag) > -1) {
27073                 return;
27074             }
27075             if (this.cwhite.indexOf(tag) > -1) {
27076                 return;
27077             }
27078             this.cwhite.push(tag);
27079             
27080         }, this);
27081         
27082         
27083         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27084             if (w.indexOf(tag) > -1) {
27085                 return;
27086             }
27087             this.cblack.push(tag);
27088             
27089         }, this);
27090         
27091         Roo.each(b, function(tag) {
27092             if (w.indexOf(tag) > -1) {
27093                 return;
27094             }
27095             if (this.cblack.indexOf(tag) > -1) {
27096                 return;
27097             }
27098             this.cblack.push(tag);
27099             
27100         }, this);
27101     },
27102     
27103     setStylesheets : function(stylesheets)
27104     {
27105         if(typeof(stylesheets) == 'string'){
27106             Roo.get(this.iframe.contentDocument.head).createChild({
27107                 tag : 'link',
27108                 rel : 'stylesheet',
27109                 type : 'text/css',
27110                 href : stylesheets
27111             });
27112             
27113             return;
27114         }
27115         var _this = this;
27116      
27117         Roo.each(stylesheets, function(s) {
27118             if(!s.length){
27119                 return;
27120             }
27121             
27122             Roo.get(_this.iframe.contentDocument.head).createChild({
27123                 tag : 'link',
27124                 rel : 'stylesheet',
27125                 type : 'text/css',
27126                 href : s
27127             });
27128         });
27129
27130         
27131     },
27132     
27133     removeStylesheets : function()
27134     {
27135         var _this = this;
27136         
27137         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27138             s.remove();
27139         });
27140     },
27141     
27142     setStyle : function(style)
27143     {
27144         Roo.get(this.iframe.contentDocument.head).createChild({
27145             tag : 'style',
27146             type : 'text/css',
27147             html : style
27148         });
27149
27150         return;
27151     }
27152     
27153     // hide stuff that is not compatible
27154     /**
27155      * @event blur
27156      * @hide
27157      */
27158     /**
27159      * @event change
27160      * @hide
27161      */
27162     /**
27163      * @event focus
27164      * @hide
27165      */
27166     /**
27167      * @event specialkey
27168      * @hide
27169      */
27170     /**
27171      * @cfg {String} fieldClass @hide
27172      */
27173     /**
27174      * @cfg {String} focusClass @hide
27175      */
27176     /**
27177      * @cfg {String} autoCreate @hide
27178      */
27179     /**
27180      * @cfg {String} inputType @hide
27181      */
27182     /**
27183      * @cfg {String} invalidClass @hide
27184      */
27185     /**
27186      * @cfg {String} invalidText @hide
27187      */
27188     /**
27189      * @cfg {String} msgFx @hide
27190      */
27191     /**
27192      * @cfg {String} validateOnBlur @hide
27193      */
27194 });
27195
27196 Roo.HtmlEditorCore.white = [
27197         'area', 'br', 'img', 'input', 'hr', 'wbr',
27198         
27199        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27200        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27201        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27202        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27203        'table',   'ul',         'xmp', 
27204        
27205        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27206       'thead',   'tr', 
27207      
27208       'dir', 'menu', 'ol', 'ul', 'dl',
27209        
27210       'embed',  'object'
27211 ];
27212
27213
27214 Roo.HtmlEditorCore.black = [
27215     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27216         'applet', // 
27217         'base',   'basefont', 'bgsound', 'blink',  'body', 
27218         'frame',  'frameset', 'head',    'html',   'ilayer', 
27219         'iframe', 'layer',  'link',     'meta',    'object',   
27220         'script', 'style' ,'title',  'xml' // clean later..
27221 ];
27222 Roo.HtmlEditorCore.clean = [
27223     'script', 'style', 'title', 'xml'
27224 ];
27225 Roo.HtmlEditorCore.remove = [
27226     'font'
27227 ];
27228 // attributes..
27229
27230 Roo.HtmlEditorCore.ablack = [
27231     'on'
27232 ];
27233     
27234 Roo.HtmlEditorCore.aclean = [ 
27235     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27236 ];
27237
27238 // protocols..
27239 Roo.HtmlEditorCore.pwhite= [
27240         'http',  'https',  'mailto'
27241 ];
27242
27243 // white listed style attributes.
27244 Roo.HtmlEditorCore.cwhite= [
27245       //  'text-align', /// default is to allow most things..
27246       
27247          
27248 //        'font-size'//??
27249 ];
27250
27251 // black listed style attributes.
27252 Roo.HtmlEditorCore.cblack= [
27253       //  'font-size' -- this can be set by the project 
27254 ];
27255
27256
27257 Roo.HtmlEditorCore.swapCodes   =[ 
27258     [    8211, "&#8211;" ], 
27259     [    8212, "&#8212;" ], 
27260     [    8216,  "'" ],  
27261     [    8217, "'" ],  
27262     [    8220, '"' ],  
27263     [    8221, '"' ],  
27264     [    8226, "*" ],  
27265     [    8230, "..." ]
27266 ]; 
27267
27268     /*
27269  * - LGPL
27270  *
27271  * HtmlEditor
27272  * 
27273  */
27274
27275 /**
27276  * @class Roo.bootstrap.HtmlEditor
27277  * @extends Roo.bootstrap.TextArea
27278  * Bootstrap HtmlEditor class
27279
27280  * @constructor
27281  * Create a new HtmlEditor
27282  * @param {Object} config The config object
27283  */
27284
27285 Roo.bootstrap.HtmlEditor = function(config){
27286     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27287     if (!this.toolbars) {
27288         this.toolbars = [];
27289     }
27290     
27291     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27292     this.addEvents({
27293             /**
27294              * @event initialize
27295              * Fires when the editor is fully initialized (including the iframe)
27296              * @param {HtmlEditor} this
27297              */
27298             initialize: true,
27299             /**
27300              * @event activate
27301              * Fires when the editor is first receives the focus. Any insertion must wait
27302              * until after this event.
27303              * @param {HtmlEditor} this
27304              */
27305             activate: true,
27306              /**
27307              * @event beforesync
27308              * Fires before the textarea is updated with content from the editor iframe. Return false
27309              * to cancel the sync.
27310              * @param {HtmlEditor} this
27311              * @param {String} html
27312              */
27313             beforesync: true,
27314              /**
27315              * @event beforepush
27316              * Fires before the iframe editor is updated with content from the textarea. Return false
27317              * to cancel the push.
27318              * @param {HtmlEditor} this
27319              * @param {String} html
27320              */
27321             beforepush: true,
27322              /**
27323              * @event sync
27324              * Fires when the textarea is updated with content from the editor iframe.
27325              * @param {HtmlEditor} this
27326              * @param {String} html
27327              */
27328             sync: true,
27329              /**
27330              * @event push
27331              * Fires when the iframe editor is updated with content from the textarea.
27332              * @param {HtmlEditor} this
27333              * @param {String} html
27334              */
27335             push: true,
27336              /**
27337              * @event editmodechange
27338              * Fires when the editor switches edit modes
27339              * @param {HtmlEditor} this
27340              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27341              */
27342             editmodechange: true,
27343             /**
27344              * @event editorevent
27345              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27346              * @param {HtmlEditor} this
27347              */
27348             editorevent: true,
27349             /**
27350              * @event firstfocus
27351              * Fires when on first focus - needed by toolbars..
27352              * @param {HtmlEditor} this
27353              */
27354             firstfocus: true,
27355             /**
27356              * @event autosave
27357              * Auto save the htmlEditor value as a file into Events
27358              * @param {HtmlEditor} this
27359              */
27360             autosave: true,
27361             /**
27362              * @event savedpreview
27363              * preview the saved version of htmlEditor
27364              * @param {HtmlEditor} this
27365              */
27366             savedpreview: true
27367         });
27368 };
27369
27370
27371 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27372     
27373     
27374       /**
27375      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27376      */
27377     toolbars : false,
27378     
27379      /**
27380     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27381     */
27382     btns : [],
27383    
27384      /**
27385      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27386      *                        Roo.resizable.
27387      */
27388     resizable : false,
27389      /**
27390      * @cfg {Number} height (in pixels)
27391      */   
27392     height: 300,
27393    /**
27394      * @cfg {Number} width (in pixels)
27395      */   
27396     width: false,
27397     
27398     /**
27399      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27400      * 
27401      */
27402     stylesheets: false,
27403     
27404     // id of frame..
27405     frameId: false,
27406     
27407     // private properties
27408     validationEvent : false,
27409     deferHeight: true,
27410     initialized : false,
27411     activated : false,
27412     
27413     onFocus : Roo.emptyFn,
27414     iframePad:3,
27415     hideMode:'offsets',
27416     
27417     tbContainer : false,
27418     
27419     bodyCls : '',
27420     
27421     toolbarContainer :function() {
27422         return this.wrap.select('.x-html-editor-tb',true).first();
27423     },
27424
27425     /**
27426      * Protected method that will not generally be called directly. It
27427      * is called when the editor creates its toolbar. Override this method if you need to
27428      * add custom toolbar buttons.
27429      * @param {HtmlEditor} editor
27430      */
27431     createToolbar : function(){
27432         Roo.log('renewing');
27433         Roo.log("create toolbars");
27434         
27435         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27436         this.toolbars[0].render(this.toolbarContainer());
27437         
27438         return;
27439         
27440 //        if (!editor.toolbars || !editor.toolbars.length) {
27441 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27442 //        }
27443 //        
27444 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27445 //            editor.toolbars[i] = Roo.factory(
27446 //                    typeof(editor.toolbars[i]) == 'string' ?
27447 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27448 //                Roo.bootstrap.HtmlEditor);
27449 //            editor.toolbars[i].init(editor);
27450 //        }
27451     },
27452
27453      
27454     // private
27455     onRender : function(ct, position)
27456     {
27457        // Roo.log("Call onRender: " + this.xtype);
27458         var _t = this;
27459         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27460       
27461         this.wrap = this.inputEl().wrap({
27462             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27463         });
27464         
27465         this.editorcore.onRender(ct, position);
27466          
27467         if (this.resizable) {
27468             this.resizeEl = new Roo.Resizable(this.wrap, {
27469                 pinned : true,
27470                 wrap: true,
27471                 dynamic : true,
27472                 minHeight : this.height,
27473                 height: this.height,
27474                 handles : this.resizable,
27475                 width: this.width,
27476                 listeners : {
27477                     resize : function(r, w, h) {
27478                         _t.onResize(w,h); // -something
27479                     }
27480                 }
27481             });
27482             
27483         }
27484         this.createToolbar(this);
27485        
27486         
27487         if(!this.width && this.resizable){
27488             this.setSize(this.wrap.getSize());
27489         }
27490         if (this.resizeEl) {
27491             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27492             // should trigger onReize..
27493         }
27494         
27495     },
27496
27497     // private
27498     onResize : function(w, h)
27499     {
27500         Roo.log('resize: ' +w + ',' + h );
27501         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27502         var ew = false;
27503         var eh = false;
27504         
27505         if(this.inputEl() ){
27506             if(typeof w == 'number'){
27507                 var aw = w - this.wrap.getFrameWidth('lr');
27508                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27509                 ew = aw;
27510             }
27511             if(typeof h == 'number'){
27512                  var tbh = -11;  // fixme it needs to tool bar size!
27513                 for (var i =0; i < this.toolbars.length;i++) {
27514                     // fixme - ask toolbars for heights?
27515                     tbh += this.toolbars[i].el.getHeight();
27516                     //if (this.toolbars[i].footer) {
27517                     //    tbh += this.toolbars[i].footer.el.getHeight();
27518                     //}
27519                 }
27520               
27521                 
27522                 
27523                 
27524                 
27525                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27526                 ah -= 5; // knock a few pixes off for look..
27527                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27528                 var eh = ah;
27529             }
27530         }
27531         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27532         this.editorcore.onResize(ew,eh);
27533         
27534     },
27535
27536     /**
27537      * Toggles the editor between standard and source edit mode.
27538      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27539      */
27540     toggleSourceEdit : function(sourceEditMode)
27541     {
27542         this.editorcore.toggleSourceEdit(sourceEditMode);
27543         
27544         if(this.editorcore.sourceEditMode){
27545             Roo.log('editor - showing textarea');
27546             
27547 //            Roo.log('in');
27548 //            Roo.log(this.syncValue());
27549             this.syncValue();
27550             this.inputEl().removeClass(['hide', 'x-hidden']);
27551             this.inputEl().dom.removeAttribute('tabIndex');
27552             this.inputEl().focus();
27553         }else{
27554             Roo.log('editor - hiding textarea');
27555 //            Roo.log('out')
27556 //            Roo.log(this.pushValue()); 
27557             this.pushValue();
27558             
27559             this.inputEl().addClass(['hide', 'x-hidden']);
27560             this.inputEl().dom.setAttribute('tabIndex', -1);
27561             //this.deferFocus();
27562         }
27563          
27564         if(this.resizable){
27565             this.setSize(this.wrap.getSize());
27566         }
27567         
27568         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27569     },
27570  
27571     // private (for BoxComponent)
27572     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27573
27574     // private (for BoxComponent)
27575     getResizeEl : function(){
27576         return this.wrap;
27577     },
27578
27579     // private (for BoxComponent)
27580     getPositionEl : function(){
27581         return this.wrap;
27582     },
27583
27584     // private
27585     initEvents : function(){
27586         this.originalValue = this.getValue();
27587     },
27588
27589 //    /**
27590 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27591 //     * @method
27592 //     */
27593 //    markInvalid : Roo.emptyFn,
27594 //    /**
27595 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27596 //     * @method
27597 //     */
27598 //    clearInvalid : Roo.emptyFn,
27599
27600     setValue : function(v){
27601         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27602         this.editorcore.pushValue();
27603     },
27604
27605      
27606     // private
27607     deferFocus : function(){
27608         this.focus.defer(10, this);
27609     },
27610
27611     // doc'ed in Field
27612     focus : function(){
27613         this.editorcore.focus();
27614         
27615     },
27616       
27617
27618     // private
27619     onDestroy : function(){
27620         
27621         
27622         
27623         if(this.rendered){
27624             
27625             for (var i =0; i < this.toolbars.length;i++) {
27626                 // fixme - ask toolbars for heights?
27627                 this.toolbars[i].onDestroy();
27628             }
27629             
27630             this.wrap.dom.innerHTML = '';
27631             this.wrap.remove();
27632         }
27633     },
27634
27635     // private
27636     onFirstFocus : function(){
27637         //Roo.log("onFirstFocus");
27638         this.editorcore.onFirstFocus();
27639          for (var i =0; i < this.toolbars.length;i++) {
27640             this.toolbars[i].onFirstFocus();
27641         }
27642         
27643     },
27644     
27645     // private
27646     syncValue : function()
27647     {   
27648         this.editorcore.syncValue();
27649     },
27650     
27651     pushValue : function()
27652     {   
27653         this.editorcore.pushValue();
27654     }
27655      
27656     
27657     // hide stuff that is not compatible
27658     /**
27659      * @event blur
27660      * @hide
27661      */
27662     /**
27663      * @event change
27664      * @hide
27665      */
27666     /**
27667      * @event focus
27668      * @hide
27669      */
27670     /**
27671      * @event specialkey
27672      * @hide
27673      */
27674     /**
27675      * @cfg {String} fieldClass @hide
27676      */
27677     /**
27678      * @cfg {String} focusClass @hide
27679      */
27680     /**
27681      * @cfg {String} autoCreate @hide
27682      */
27683     /**
27684      * @cfg {String} inputType @hide
27685      */
27686      
27687     /**
27688      * @cfg {String} invalidText @hide
27689      */
27690     /**
27691      * @cfg {String} msgFx @hide
27692      */
27693     /**
27694      * @cfg {String} validateOnBlur @hide
27695      */
27696 });
27697  
27698     
27699    
27700    
27701    
27702       
27703 Roo.namespace('Roo.bootstrap.htmleditor');
27704 /**
27705  * @class Roo.bootstrap.HtmlEditorToolbar1
27706  * Basic Toolbar
27707  * 
27708  * @example
27709  * Usage:
27710  *
27711  new Roo.bootstrap.HtmlEditor({
27712     ....
27713     toolbars : [
27714         new Roo.bootstrap.HtmlEditorToolbar1({
27715             disable : { fonts: 1 , format: 1, ..., ... , ...],
27716             btns : [ .... ]
27717         })
27718     }
27719      
27720  * 
27721  * @cfg {Object} disable List of elements to disable..
27722  * @cfg {Array} btns List of additional buttons.
27723  * 
27724  * 
27725  * NEEDS Extra CSS? 
27726  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27727  */
27728  
27729 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27730 {
27731     
27732     Roo.apply(this, config);
27733     
27734     // default disabled, based on 'good practice'..
27735     this.disable = this.disable || {};
27736     Roo.applyIf(this.disable, {
27737         fontSize : true,
27738         colors : true,
27739         specialElements : true
27740     });
27741     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27742     
27743     this.editor = config.editor;
27744     this.editorcore = config.editor.editorcore;
27745     
27746     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27747     
27748     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27749     // dont call parent... till later.
27750 }
27751 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27752      
27753     bar : true,
27754     
27755     editor : false,
27756     editorcore : false,
27757     
27758     
27759     formats : [
27760         "p" ,  
27761         "h1","h2","h3","h4","h5","h6", 
27762         "pre", "code", 
27763         "abbr", "acronym", "address", "cite", "samp", "var",
27764         'div','span'
27765     ],
27766     
27767     onRender : function(ct, position)
27768     {
27769        // Roo.log("Call onRender: " + this.xtype);
27770         
27771        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27772        Roo.log(this.el);
27773        this.el.dom.style.marginBottom = '0';
27774        var _this = this;
27775        var editorcore = this.editorcore;
27776        var editor= this.editor;
27777        
27778        var children = [];
27779        var btn = function(id,cmd , toggle, handler, html){
27780        
27781             var  event = toggle ? 'toggle' : 'click';
27782        
27783             var a = {
27784                 size : 'sm',
27785                 xtype: 'Button',
27786                 xns: Roo.bootstrap,
27787                 //glyphicon : id,
27788                 fa: id,
27789                 cmd : id || cmd,
27790                 enableToggle:toggle !== false,
27791                 html : html || '',
27792                 pressed : toggle ? false : null,
27793                 listeners : {}
27794             };
27795             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27796                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27797             };
27798             children.push(a);
27799             return a;
27800        }
27801        
27802     //    var cb_box = function...
27803         
27804         var style = {
27805                 xtype: 'Button',
27806                 size : 'sm',
27807                 xns: Roo.bootstrap,
27808                 fa : 'font',
27809                 //html : 'submit'
27810                 menu : {
27811                     xtype: 'Menu',
27812                     xns: Roo.bootstrap,
27813                     items:  []
27814                 }
27815         };
27816         Roo.each(this.formats, function(f) {
27817             style.menu.items.push({
27818                 xtype :'MenuItem',
27819                 xns: Roo.bootstrap,
27820                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27821                 tagname : f,
27822                 listeners : {
27823                     click : function()
27824                     {
27825                         editorcore.insertTag(this.tagname);
27826                         editor.focus();
27827                     }
27828                 }
27829                 
27830             });
27831         });
27832         children.push(style);   
27833         
27834         btn('bold',false,true);
27835         btn('italic',false,true);
27836         btn('align-left', 'justifyleft',true);
27837         btn('align-center', 'justifycenter',true);
27838         btn('align-right' , 'justifyright',true);
27839         btn('link', false, false, function(btn) {
27840             //Roo.log("create link?");
27841             var url = prompt(this.createLinkText, this.defaultLinkValue);
27842             if(url && url != 'http:/'+'/'){
27843                 this.editorcore.relayCmd('createlink', url);
27844             }
27845         }),
27846         btn('list','insertunorderedlist',true);
27847         btn('pencil', false,true, function(btn){
27848                 Roo.log(this);
27849                 this.toggleSourceEdit(btn.pressed);
27850         });
27851         
27852         if (this.editor.btns.length > 0) {
27853             for (var i = 0; i<this.editor.btns.length; i++) {
27854                 children.push(this.editor.btns[i]);
27855             }
27856         }
27857         
27858         /*
27859         var cog = {
27860                 xtype: 'Button',
27861                 size : 'sm',
27862                 xns: Roo.bootstrap,
27863                 glyphicon : 'cog',
27864                 //html : 'submit'
27865                 menu : {
27866                     xtype: 'Menu',
27867                     xns: Roo.bootstrap,
27868                     items:  []
27869                 }
27870         };
27871         
27872         cog.menu.items.push({
27873             xtype :'MenuItem',
27874             xns: Roo.bootstrap,
27875             html : Clean styles,
27876             tagname : f,
27877             listeners : {
27878                 click : function()
27879                 {
27880                     editorcore.insertTag(this.tagname);
27881                     editor.focus();
27882                 }
27883             }
27884             
27885         });
27886        */
27887         
27888          
27889        this.xtype = 'NavSimplebar';
27890         
27891         for(var i=0;i< children.length;i++) {
27892             
27893             this.buttons.add(this.addxtypeChild(children[i]));
27894             
27895         }
27896         
27897         editor.on('editorevent', this.updateToolbar, this);
27898     },
27899     onBtnClick : function(id)
27900     {
27901        this.editorcore.relayCmd(id);
27902        this.editorcore.focus();
27903     },
27904     
27905     /**
27906      * Protected method that will not generally be called directly. It triggers
27907      * a toolbar update by reading the markup state of the current selection in the editor.
27908      */
27909     updateToolbar: function(){
27910
27911         if(!this.editorcore.activated){
27912             this.editor.onFirstFocus(); // is this neeed?
27913             return;
27914         }
27915
27916         var btns = this.buttons; 
27917         var doc = this.editorcore.doc;
27918         btns.get('bold').setActive(doc.queryCommandState('bold'));
27919         btns.get('italic').setActive(doc.queryCommandState('italic'));
27920         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27921         
27922         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27923         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27924         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27925         
27926         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27927         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27928          /*
27929         
27930         var ans = this.editorcore.getAllAncestors();
27931         if (this.formatCombo) {
27932             
27933             
27934             var store = this.formatCombo.store;
27935             this.formatCombo.setValue("");
27936             for (var i =0; i < ans.length;i++) {
27937                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27938                     // select it..
27939                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27940                     break;
27941                 }
27942             }
27943         }
27944         
27945         
27946         
27947         // hides menus... - so this cant be on a menu...
27948         Roo.bootstrap.MenuMgr.hideAll();
27949         */
27950         Roo.bootstrap.MenuMgr.hideAll();
27951         //this.editorsyncValue();
27952     },
27953     onFirstFocus: function() {
27954         this.buttons.each(function(item){
27955            item.enable();
27956         });
27957     },
27958     toggleSourceEdit : function(sourceEditMode){
27959         
27960           
27961         if(sourceEditMode){
27962             Roo.log("disabling buttons");
27963            this.buttons.each( function(item){
27964                 if(item.cmd != 'pencil'){
27965                     item.disable();
27966                 }
27967             });
27968           
27969         }else{
27970             Roo.log("enabling buttons");
27971             if(this.editorcore.initialized){
27972                 this.buttons.each( function(item){
27973                     item.enable();
27974                 });
27975             }
27976             
27977         }
27978         Roo.log("calling toggole on editor");
27979         // tell the editor that it's been pressed..
27980         this.editor.toggleSourceEdit(sourceEditMode);
27981        
27982     }
27983 });
27984
27985
27986
27987
27988  
27989 /*
27990  * - LGPL
27991  */
27992
27993 /**
27994  * @class Roo.bootstrap.Markdown
27995  * @extends Roo.bootstrap.TextArea
27996  * Bootstrap Showdown editable area
27997  * @cfg {string} content
27998  * 
27999  * @constructor
28000  * Create a new Showdown
28001  */
28002
28003 Roo.bootstrap.Markdown = function(config){
28004     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28005    
28006 };
28007
28008 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28009     
28010     editing :false,
28011     
28012     initEvents : function()
28013     {
28014         
28015         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28016         this.markdownEl = this.el.createChild({
28017             cls : 'roo-markdown-area'
28018         });
28019         this.inputEl().addClass('d-none');
28020         if (this.getValue() == '') {
28021             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28022             
28023         } else {
28024             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28025         }
28026         this.markdownEl.on('click', this.toggleTextEdit, this);
28027         this.on('blur', this.toggleTextEdit, this);
28028         this.on('specialkey', this.resizeTextArea, this);
28029     },
28030     
28031     toggleTextEdit : function()
28032     {
28033         var sh = this.markdownEl.getHeight();
28034         this.inputEl().addClass('d-none');
28035         this.markdownEl.addClass('d-none');
28036         if (!this.editing) {
28037             // show editor?
28038             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28039             this.inputEl().removeClass('d-none');
28040             this.inputEl().focus();
28041             this.editing = true;
28042             return;
28043         }
28044         // show showdown...
28045         this.updateMarkdown();
28046         this.markdownEl.removeClass('d-none');
28047         this.editing = false;
28048         return;
28049     },
28050     updateMarkdown : function()
28051     {
28052         if (this.getValue() == '') {
28053             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28054             return;
28055         }
28056  
28057         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28058     },
28059     
28060     resizeTextArea: function () {
28061         
28062         var sh = 100;
28063         Roo.log([sh, this.getValue().split("\n").length * 30]);
28064         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28065     },
28066     setValue : function(val)
28067     {
28068         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28069         if (!this.editing) {
28070             this.updateMarkdown();
28071         }
28072         
28073     },
28074     focus : function()
28075     {
28076         if (!this.editing) {
28077             this.toggleTextEdit();
28078         }
28079         
28080     }
28081
28082
28083 });/*
28084  * Based on:
28085  * Ext JS Library 1.1.1
28086  * Copyright(c) 2006-2007, Ext JS, LLC.
28087  *
28088  * Originally Released Under LGPL - original licence link has changed is not relivant.
28089  *
28090  * Fork - LGPL
28091  * <script type="text/javascript">
28092  */
28093  
28094 /**
28095  * @class Roo.bootstrap.PagingToolbar
28096  * @extends Roo.bootstrap.NavSimplebar
28097  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28098  * @constructor
28099  * Create a new PagingToolbar
28100  * @param {Object} config The config object
28101  * @param {Roo.data.Store} store
28102  */
28103 Roo.bootstrap.PagingToolbar = function(config)
28104 {
28105     // old args format still supported... - xtype is prefered..
28106         // created from xtype...
28107     
28108     this.ds = config.dataSource;
28109     
28110     if (config.store && !this.ds) {
28111         this.store= Roo.factory(config.store, Roo.data);
28112         this.ds = this.store;
28113         this.ds.xmodule = this.xmodule || false;
28114     }
28115     
28116     this.toolbarItems = [];
28117     if (config.items) {
28118         this.toolbarItems = config.items;
28119     }
28120     
28121     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28122     
28123     this.cursor = 0;
28124     
28125     if (this.ds) { 
28126         this.bind(this.ds);
28127     }
28128     
28129     if (Roo.bootstrap.version == 4) {
28130         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28131     } else {
28132         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28133     }
28134     
28135 };
28136
28137 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28138     /**
28139      * @cfg {Roo.data.Store} dataSource
28140      * The underlying data store providing the paged data
28141      */
28142     /**
28143      * @cfg {String/HTMLElement/Element} container
28144      * container The id or element that will contain the toolbar
28145      */
28146     /**
28147      * @cfg {Boolean} displayInfo
28148      * True to display the displayMsg (defaults to false)
28149      */
28150     /**
28151      * @cfg {Number} pageSize
28152      * The number of records to display per page (defaults to 20)
28153      */
28154     pageSize: 20,
28155     /**
28156      * @cfg {String} displayMsg
28157      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28158      */
28159     displayMsg : 'Displaying {0} - {1} of {2}',
28160     /**
28161      * @cfg {String} emptyMsg
28162      * The message to display when no records are found (defaults to "No data to display")
28163      */
28164     emptyMsg : 'No data to display',
28165     /**
28166      * Customizable piece of the default paging text (defaults to "Page")
28167      * @type String
28168      */
28169     beforePageText : "Page",
28170     /**
28171      * Customizable piece of the default paging text (defaults to "of %0")
28172      * @type String
28173      */
28174     afterPageText : "of {0}",
28175     /**
28176      * Customizable piece of the default paging text (defaults to "First Page")
28177      * @type String
28178      */
28179     firstText : "First Page",
28180     /**
28181      * Customizable piece of the default paging text (defaults to "Previous Page")
28182      * @type String
28183      */
28184     prevText : "Previous Page",
28185     /**
28186      * Customizable piece of the default paging text (defaults to "Next Page")
28187      * @type String
28188      */
28189     nextText : "Next Page",
28190     /**
28191      * Customizable piece of the default paging text (defaults to "Last Page")
28192      * @type String
28193      */
28194     lastText : "Last Page",
28195     /**
28196      * Customizable piece of the default paging text (defaults to "Refresh")
28197      * @type String
28198      */
28199     refreshText : "Refresh",
28200
28201     buttons : false,
28202     // private
28203     onRender : function(ct, position) 
28204     {
28205         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28206         this.navgroup.parentId = this.id;
28207         this.navgroup.onRender(this.el, null);
28208         // add the buttons to the navgroup
28209         
28210         if(this.displayInfo){
28211             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28212             this.displayEl = this.el.select('.x-paging-info', true).first();
28213 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28214 //            this.displayEl = navel.el.select('span',true).first();
28215         }
28216         
28217         var _this = this;
28218         
28219         if(this.buttons){
28220             Roo.each(_this.buttons, function(e){ // this might need to use render????
28221                Roo.factory(e).render(_this.el);
28222             });
28223         }
28224             
28225         Roo.each(_this.toolbarItems, function(e) {
28226             _this.navgroup.addItem(e);
28227         });
28228         
28229         
28230         this.first = this.navgroup.addItem({
28231             tooltip: this.firstText,
28232             cls: "prev btn-outline-secondary",
28233             html : ' <i class="fa fa-step-backward"></i>',
28234             disabled: true,
28235             preventDefault: true,
28236             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28237         });
28238         
28239         this.prev =  this.navgroup.addItem({
28240             tooltip: this.prevText,
28241             cls: "prev btn-outline-secondary",
28242             html : ' <i class="fa fa-backward"></i>',
28243             disabled: true,
28244             preventDefault: true,
28245             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28246         });
28247     //this.addSeparator();
28248         
28249         
28250         var field = this.navgroup.addItem( {
28251             tagtype : 'span',
28252             cls : 'x-paging-position  btn-outline-secondary',
28253              disabled: true,
28254             html : this.beforePageText  +
28255                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28256                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28257          } ); //?? escaped?
28258         
28259         this.field = field.el.select('input', true).first();
28260         this.field.on("keydown", this.onPagingKeydown, this);
28261         this.field.on("focus", function(){this.dom.select();});
28262     
28263     
28264         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28265         //this.field.setHeight(18);
28266         //this.addSeparator();
28267         this.next = this.navgroup.addItem({
28268             tooltip: this.nextText,
28269             cls: "next btn-outline-secondary",
28270             html : ' <i class="fa fa-forward"></i>',
28271             disabled: true,
28272             preventDefault: true,
28273             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28274         });
28275         this.last = this.navgroup.addItem({
28276             tooltip: this.lastText,
28277             html : ' <i class="fa fa-step-forward"></i>',
28278             cls: "next btn-outline-secondary",
28279             disabled: true,
28280             preventDefault: true,
28281             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28282         });
28283     //this.addSeparator();
28284         this.loading = this.navgroup.addItem({
28285             tooltip: this.refreshText,
28286             cls: "btn-outline-secondary",
28287             html : ' <i class="fa fa-refresh"></i>',
28288             preventDefault: true,
28289             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28290         });
28291         
28292     },
28293
28294     // private
28295     updateInfo : function(){
28296         if(this.displayEl){
28297             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28298             var msg = count == 0 ?
28299                 this.emptyMsg :
28300                 String.format(
28301                     this.displayMsg,
28302                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28303                 );
28304             this.displayEl.update(msg);
28305         }
28306     },
28307
28308     // private
28309     onLoad : function(ds, r, o)
28310     {
28311         this.cursor = o.params && o.params.start ? o.params.start : 0;
28312         
28313         var d = this.getPageData(),
28314             ap = d.activePage,
28315             ps = d.pages;
28316         
28317         
28318         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28319         this.field.dom.value = ap;
28320         this.first.setDisabled(ap == 1);
28321         this.prev.setDisabled(ap == 1);
28322         this.next.setDisabled(ap == ps);
28323         this.last.setDisabled(ap == ps);
28324         this.loading.enable();
28325         this.updateInfo();
28326     },
28327
28328     // private
28329     getPageData : function(){
28330         var total = this.ds.getTotalCount();
28331         return {
28332             total : total,
28333             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28334             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28335         };
28336     },
28337
28338     // private
28339     onLoadError : function(){
28340         this.loading.enable();
28341     },
28342
28343     // private
28344     onPagingKeydown : function(e){
28345         var k = e.getKey();
28346         var d = this.getPageData();
28347         if(k == e.RETURN){
28348             var v = this.field.dom.value, pageNum;
28349             if(!v || isNaN(pageNum = parseInt(v, 10))){
28350                 this.field.dom.value = d.activePage;
28351                 return;
28352             }
28353             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28354             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28355             e.stopEvent();
28356         }
28357         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))
28358         {
28359           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28360           this.field.dom.value = pageNum;
28361           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28362           e.stopEvent();
28363         }
28364         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28365         {
28366           var v = this.field.dom.value, pageNum; 
28367           var increment = (e.shiftKey) ? 10 : 1;
28368           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28369                 increment *= -1;
28370           }
28371           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28372             this.field.dom.value = d.activePage;
28373             return;
28374           }
28375           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28376           {
28377             this.field.dom.value = parseInt(v, 10) + increment;
28378             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28379             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28380           }
28381           e.stopEvent();
28382         }
28383     },
28384
28385     // private
28386     beforeLoad : function(){
28387         if(this.loading){
28388             this.loading.disable();
28389         }
28390     },
28391
28392     // private
28393     onClick : function(which){
28394         
28395         var ds = this.ds;
28396         if (!ds) {
28397             return;
28398         }
28399         
28400         switch(which){
28401             case "first":
28402                 ds.load({params:{start: 0, limit: this.pageSize}});
28403             break;
28404             case "prev":
28405                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28406             break;
28407             case "next":
28408                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28409             break;
28410             case "last":
28411                 var total = ds.getTotalCount();
28412                 var extra = total % this.pageSize;
28413                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28414                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28415             break;
28416             case "refresh":
28417                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28418             break;
28419         }
28420     },
28421
28422     /**
28423      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28424      * @param {Roo.data.Store} store The data store to unbind
28425      */
28426     unbind : function(ds){
28427         ds.un("beforeload", this.beforeLoad, this);
28428         ds.un("load", this.onLoad, this);
28429         ds.un("loadexception", this.onLoadError, this);
28430         ds.un("remove", this.updateInfo, this);
28431         ds.un("add", this.updateInfo, this);
28432         this.ds = undefined;
28433     },
28434
28435     /**
28436      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28437      * @param {Roo.data.Store} store The data store to bind
28438      */
28439     bind : function(ds){
28440         ds.on("beforeload", this.beforeLoad, this);
28441         ds.on("load", this.onLoad, this);
28442         ds.on("loadexception", this.onLoadError, this);
28443         ds.on("remove", this.updateInfo, this);
28444         ds.on("add", this.updateInfo, this);
28445         this.ds = ds;
28446     }
28447 });/*
28448  * - LGPL
28449  *
28450  * element
28451  * 
28452  */
28453
28454 /**
28455  * @class Roo.bootstrap.MessageBar
28456  * @extends Roo.bootstrap.Component
28457  * Bootstrap MessageBar class
28458  * @cfg {String} html contents of the MessageBar
28459  * @cfg {String} weight (info | success | warning | danger) default info
28460  * @cfg {String} beforeClass insert the bar before the given class
28461  * @cfg {Boolean} closable (true | false) default false
28462  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28463  * 
28464  * @constructor
28465  * Create a new Element
28466  * @param {Object} config The config object
28467  */
28468
28469 Roo.bootstrap.MessageBar = function(config){
28470     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28471 };
28472
28473 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28474     
28475     html: '',
28476     weight: 'info',
28477     closable: false,
28478     fixed: false,
28479     beforeClass: 'bootstrap-sticky-wrap',
28480     
28481     getAutoCreate : function(){
28482         
28483         var cfg = {
28484             tag: 'div',
28485             cls: 'alert alert-dismissable alert-' + this.weight,
28486             cn: [
28487                 {
28488                     tag: 'span',
28489                     cls: 'message',
28490                     html: this.html || ''
28491                 }
28492             ]
28493         };
28494         
28495         if(this.fixed){
28496             cfg.cls += ' alert-messages-fixed';
28497         }
28498         
28499         if(this.closable){
28500             cfg.cn.push({
28501                 tag: 'button',
28502                 cls: 'close',
28503                 html: 'x'
28504             });
28505         }
28506         
28507         return cfg;
28508     },
28509     
28510     onRender : function(ct, position)
28511     {
28512         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28513         
28514         if(!this.el){
28515             var cfg = Roo.apply({},  this.getAutoCreate());
28516             cfg.id = Roo.id();
28517             
28518             if (this.cls) {
28519                 cfg.cls += ' ' + this.cls;
28520             }
28521             if (this.style) {
28522                 cfg.style = this.style;
28523             }
28524             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28525             
28526             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28527         }
28528         
28529         this.el.select('>button.close').on('click', this.hide, this);
28530         
28531     },
28532     
28533     show : function()
28534     {
28535         if (!this.rendered) {
28536             this.render();
28537         }
28538         
28539         this.el.show();
28540         
28541         this.fireEvent('show', this);
28542         
28543     },
28544     
28545     hide : function()
28546     {
28547         if (!this.rendered) {
28548             this.render();
28549         }
28550         
28551         this.el.hide();
28552         
28553         this.fireEvent('hide', this);
28554     },
28555     
28556     update : function()
28557     {
28558 //        var e = this.el.dom.firstChild;
28559 //        
28560 //        if(this.closable){
28561 //            e = e.nextSibling;
28562 //        }
28563 //        
28564 //        e.data = this.html || '';
28565
28566         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28567     }
28568    
28569 });
28570
28571  
28572
28573      /*
28574  * - LGPL
28575  *
28576  * Graph
28577  * 
28578  */
28579
28580
28581 /**
28582  * @class Roo.bootstrap.Graph
28583  * @extends Roo.bootstrap.Component
28584  * Bootstrap Graph class
28585 > Prameters
28586  -sm {number} sm 4
28587  -md {number} md 5
28588  @cfg {String} graphtype  bar | vbar | pie
28589  @cfg {number} g_x coodinator | centre x (pie)
28590  @cfg {number} g_y coodinator | centre y (pie)
28591  @cfg {number} g_r radius (pie)
28592  @cfg {number} g_height height of the chart (respected by all elements in the set)
28593  @cfg {number} g_width width of the chart (respected by all elements in the set)
28594  @cfg {Object} title The title of the chart
28595     
28596  -{Array}  values
28597  -opts (object) options for the chart 
28598      o {
28599      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28600      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28601      o vgutter (number)
28602      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.
28603      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28604      o to
28605      o stretch (boolean)
28606      o }
28607  -opts (object) options for the pie
28608      o{
28609      o cut
28610      o startAngle (number)
28611      o endAngle (number)
28612      } 
28613  *
28614  * @constructor
28615  * Create a new Input
28616  * @param {Object} config The config object
28617  */
28618
28619 Roo.bootstrap.Graph = function(config){
28620     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28621     
28622     this.addEvents({
28623         // img events
28624         /**
28625          * @event click
28626          * The img click event for the img.
28627          * @param {Roo.EventObject} e
28628          */
28629         "click" : true
28630     });
28631 };
28632
28633 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28634     
28635     sm: 4,
28636     md: 5,
28637     graphtype: 'bar',
28638     g_height: 250,
28639     g_width: 400,
28640     g_x: 50,
28641     g_y: 50,
28642     g_r: 30,
28643     opts:{
28644         //g_colors: this.colors,
28645         g_type: 'soft',
28646         g_gutter: '20%'
28647
28648     },
28649     title : false,
28650
28651     getAutoCreate : function(){
28652         
28653         var cfg = {
28654             tag: 'div',
28655             html : null
28656         };
28657         
28658         
28659         return  cfg;
28660     },
28661
28662     onRender : function(ct,position){
28663         
28664         
28665         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28666         
28667         if (typeof(Raphael) == 'undefined') {
28668             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28669             return;
28670         }
28671         
28672         this.raphael = Raphael(this.el.dom);
28673         
28674                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28675                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28676                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28677                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28678                 /*
28679                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28680                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28681                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28682                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28683                 
28684                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28685                 r.barchart(330, 10, 300, 220, data1);
28686                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28687                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28688                 */
28689                 
28690                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28691                 // r.barchart(30, 30, 560, 250,  xdata, {
28692                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28693                 //     axis : "0 0 1 1",
28694                 //     axisxlabels :  xdata
28695                 //     //yvalues : cols,
28696                    
28697                 // });
28698 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28699 //        
28700 //        this.load(null,xdata,{
28701 //                axis : "0 0 1 1",
28702 //                axisxlabels :  xdata
28703 //                });
28704
28705     },
28706
28707     load : function(graphtype,xdata,opts)
28708     {
28709         this.raphael.clear();
28710         if(!graphtype) {
28711             graphtype = this.graphtype;
28712         }
28713         if(!opts){
28714             opts = this.opts;
28715         }
28716         var r = this.raphael,
28717             fin = function () {
28718                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28719             },
28720             fout = function () {
28721                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28722             },
28723             pfin = function() {
28724                 this.sector.stop();
28725                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28726
28727                 if (this.label) {
28728                     this.label[0].stop();
28729                     this.label[0].attr({ r: 7.5 });
28730                     this.label[1].attr({ "font-weight": 800 });
28731                 }
28732             },
28733             pfout = function() {
28734                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28735
28736                 if (this.label) {
28737                     this.label[0].animate({ r: 5 }, 500, "bounce");
28738                     this.label[1].attr({ "font-weight": 400 });
28739                 }
28740             };
28741
28742         switch(graphtype){
28743             case 'bar':
28744                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28745                 break;
28746             case 'hbar':
28747                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28748                 break;
28749             case 'pie':
28750 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28751 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28752 //            
28753                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28754                 
28755                 break;
28756
28757         }
28758         
28759         if(this.title){
28760             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28761         }
28762         
28763     },
28764     
28765     setTitle: function(o)
28766     {
28767         this.title = o;
28768     },
28769     
28770     initEvents: function() {
28771         
28772         if(!this.href){
28773             this.el.on('click', this.onClick, this);
28774         }
28775     },
28776     
28777     onClick : function(e)
28778     {
28779         Roo.log('img onclick');
28780         this.fireEvent('click', this, e);
28781     }
28782    
28783 });
28784
28785  
28786 /*
28787  * - LGPL
28788  *
28789  * numberBox
28790  * 
28791  */
28792 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28793
28794 /**
28795  * @class Roo.bootstrap.dash.NumberBox
28796  * @extends Roo.bootstrap.Component
28797  * Bootstrap NumberBox class
28798  * @cfg {String} headline Box headline
28799  * @cfg {String} content Box content
28800  * @cfg {String} icon Box icon
28801  * @cfg {String} footer Footer text
28802  * @cfg {String} fhref Footer href
28803  * 
28804  * @constructor
28805  * Create a new NumberBox
28806  * @param {Object} config The config object
28807  */
28808
28809
28810 Roo.bootstrap.dash.NumberBox = function(config){
28811     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28812     
28813 };
28814
28815 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28816     
28817     headline : '',
28818     content : '',
28819     icon : '',
28820     footer : '',
28821     fhref : '',
28822     ficon : '',
28823     
28824     getAutoCreate : function(){
28825         
28826         var cfg = {
28827             tag : 'div',
28828             cls : 'small-box ',
28829             cn : [
28830                 {
28831                     tag : 'div',
28832                     cls : 'inner',
28833                     cn :[
28834                         {
28835                             tag : 'h3',
28836                             cls : 'roo-headline',
28837                             html : this.headline
28838                         },
28839                         {
28840                             tag : 'p',
28841                             cls : 'roo-content',
28842                             html : this.content
28843                         }
28844                     ]
28845                 }
28846             ]
28847         };
28848         
28849         if(this.icon){
28850             cfg.cn.push({
28851                 tag : 'div',
28852                 cls : 'icon',
28853                 cn :[
28854                     {
28855                         tag : 'i',
28856                         cls : 'ion ' + this.icon
28857                     }
28858                 ]
28859             });
28860         }
28861         
28862         if(this.footer){
28863             var footer = {
28864                 tag : 'a',
28865                 cls : 'small-box-footer',
28866                 href : this.fhref || '#',
28867                 html : this.footer
28868             };
28869             
28870             cfg.cn.push(footer);
28871             
28872         }
28873         
28874         return  cfg;
28875     },
28876
28877     onRender : function(ct,position){
28878         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28879
28880
28881        
28882                 
28883     },
28884
28885     setHeadline: function (value)
28886     {
28887         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28888     },
28889     
28890     setFooter: function (value, href)
28891     {
28892         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28893         
28894         if(href){
28895             this.el.select('a.small-box-footer',true).first().attr('href', href);
28896         }
28897         
28898     },
28899
28900     setContent: function (value)
28901     {
28902         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28903     },
28904
28905     initEvents: function() 
28906     {   
28907         
28908     }
28909     
28910 });
28911
28912  
28913 /*
28914  * - LGPL
28915  *
28916  * TabBox
28917  * 
28918  */
28919 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28920
28921 /**
28922  * @class Roo.bootstrap.dash.TabBox
28923  * @extends Roo.bootstrap.Component
28924  * Bootstrap TabBox class
28925  * @cfg {String} title Title of the TabBox
28926  * @cfg {String} icon Icon of the TabBox
28927  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28928  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28929  * 
28930  * @constructor
28931  * Create a new TabBox
28932  * @param {Object} config The config object
28933  */
28934
28935
28936 Roo.bootstrap.dash.TabBox = function(config){
28937     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28938     this.addEvents({
28939         // raw events
28940         /**
28941          * @event addpane
28942          * When a pane is added
28943          * @param {Roo.bootstrap.dash.TabPane} pane
28944          */
28945         "addpane" : true,
28946         /**
28947          * @event activatepane
28948          * When a pane is activated
28949          * @param {Roo.bootstrap.dash.TabPane} pane
28950          */
28951         "activatepane" : true
28952         
28953          
28954     });
28955     
28956     this.panes = [];
28957 };
28958
28959 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28960
28961     title : '',
28962     icon : false,
28963     showtabs : true,
28964     tabScrollable : false,
28965     
28966     getChildContainer : function()
28967     {
28968         return this.el.select('.tab-content', true).first();
28969     },
28970     
28971     getAutoCreate : function(){
28972         
28973         var header = {
28974             tag: 'li',
28975             cls: 'pull-left header',
28976             html: this.title,
28977             cn : []
28978         };
28979         
28980         if(this.icon){
28981             header.cn.push({
28982                 tag: 'i',
28983                 cls: 'fa ' + this.icon
28984             });
28985         }
28986         
28987         var h = {
28988             tag: 'ul',
28989             cls: 'nav nav-tabs pull-right',
28990             cn: [
28991                 header
28992             ]
28993         };
28994         
28995         if(this.tabScrollable){
28996             h = {
28997                 tag: 'div',
28998                 cls: 'tab-header',
28999                 cn: [
29000                     {
29001                         tag: 'ul',
29002                         cls: 'nav nav-tabs pull-right',
29003                         cn: [
29004                             header
29005                         ]
29006                     }
29007                 ]
29008             };
29009         }
29010         
29011         var cfg = {
29012             tag: 'div',
29013             cls: 'nav-tabs-custom',
29014             cn: [
29015                 h,
29016                 {
29017                     tag: 'div',
29018                     cls: 'tab-content no-padding',
29019                     cn: []
29020                 }
29021             ]
29022         };
29023
29024         return  cfg;
29025     },
29026     initEvents : function()
29027     {
29028         //Roo.log('add add pane handler');
29029         this.on('addpane', this.onAddPane, this);
29030     },
29031      /**
29032      * Updates the box title
29033      * @param {String} html to set the title to.
29034      */
29035     setTitle : function(value)
29036     {
29037         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29038     },
29039     onAddPane : function(pane)
29040     {
29041         this.panes.push(pane);
29042         //Roo.log('addpane');
29043         //Roo.log(pane);
29044         // tabs are rendere left to right..
29045         if(!this.showtabs){
29046             return;
29047         }
29048         
29049         var ctr = this.el.select('.nav-tabs', true).first();
29050          
29051          
29052         var existing = ctr.select('.nav-tab',true);
29053         var qty = existing.getCount();;
29054         
29055         
29056         var tab = ctr.createChild({
29057             tag : 'li',
29058             cls : 'nav-tab' + (qty ? '' : ' active'),
29059             cn : [
29060                 {
29061                     tag : 'a',
29062                     href:'#',
29063                     html : pane.title
29064                 }
29065             ]
29066         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29067         pane.tab = tab;
29068         
29069         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29070         if (!qty) {
29071             pane.el.addClass('active');
29072         }
29073         
29074                 
29075     },
29076     onTabClick : function(ev,un,ob,pane)
29077     {
29078         //Roo.log('tab - prev default');
29079         ev.preventDefault();
29080         
29081         
29082         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29083         pane.tab.addClass('active');
29084         //Roo.log(pane.title);
29085         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29086         // technically we should have a deactivate event.. but maybe add later.
29087         // and it should not de-activate the selected tab...
29088         this.fireEvent('activatepane', pane);
29089         pane.el.addClass('active');
29090         pane.fireEvent('activate');
29091         
29092         
29093     },
29094     
29095     getActivePane : function()
29096     {
29097         var r = false;
29098         Roo.each(this.panes, function(p) {
29099             if(p.el.hasClass('active')){
29100                 r = p;
29101                 return false;
29102             }
29103             
29104             return;
29105         });
29106         
29107         return r;
29108     }
29109     
29110     
29111 });
29112
29113  
29114 /*
29115  * - LGPL
29116  *
29117  * Tab pane
29118  * 
29119  */
29120 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29121 /**
29122  * @class Roo.bootstrap.TabPane
29123  * @extends Roo.bootstrap.Component
29124  * Bootstrap TabPane class
29125  * @cfg {Boolean} active (false | true) Default false
29126  * @cfg {String} title title of panel
29127
29128  * 
29129  * @constructor
29130  * Create a new TabPane
29131  * @param {Object} config The config object
29132  */
29133
29134 Roo.bootstrap.dash.TabPane = function(config){
29135     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29136     
29137     this.addEvents({
29138         // raw events
29139         /**
29140          * @event activate
29141          * When a pane is activated
29142          * @param {Roo.bootstrap.dash.TabPane} pane
29143          */
29144         "activate" : true
29145          
29146     });
29147 };
29148
29149 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29150     
29151     active : false,
29152     title : '',
29153     
29154     // the tabBox that this is attached to.
29155     tab : false,
29156      
29157     getAutoCreate : function() 
29158     {
29159         var cfg = {
29160             tag: 'div',
29161             cls: 'tab-pane'
29162         };
29163         
29164         if(this.active){
29165             cfg.cls += ' active';
29166         }
29167         
29168         return cfg;
29169     },
29170     initEvents  : function()
29171     {
29172         //Roo.log('trigger add pane handler');
29173         this.parent().fireEvent('addpane', this)
29174     },
29175     
29176      /**
29177      * Updates the tab title 
29178      * @param {String} html to set the title to.
29179      */
29180     setTitle: function(str)
29181     {
29182         if (!this.tab) {
29183             return;
29184         }
29185         this.title = str;
29186         this.tab.select('a', true).first().dom.innerHTML = str;
29187         
29188     }
29189     
29190     
29191     
29192 });
29193
29194  
29195
29196
29197  /*
29198  * - LGPL
29199  *
29200  * menu
29201  * 
29202  */
29203 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29204
29205 /**
29206  * @class Roo.bootstrap.menu.Menu
29207  * @extends Roo.bootstrap.Component
29208  * Bootstrap Menu class - container for Menu
29209  * @cfg {String} html Text of the menu
29210  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29211  * @cfg {String} icon Font awesome icon
29212  * @cfg {String} pos Menu align to (top | bottom) default bottom
29213  * 
29214  * 
29215  * @constructor
29216  * Create a new Menu
29217  * @param {Object} config The config object
29218  */
29219
29220
29221 Roo.bootstrap.menu.Menu = function(config){
29222     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29223     
29224     this.addEvents({
29225         /**
29226          * @event beforeshow
29227          * Fires before this menu is displayed
29228          * @param {Roo.bootstrap.menu.Menu} this
29229          */
29230         beforeshow : true,
29231         /**
29232          * @event beforehide
29233          * Fires before this menu is hidden
29234          * @param {Roo.bootstrap.menu.Menu} this
29235          */
29236         beforehide : true,
29237         /**
29238          * @event show
29239          * Fires after this menu is displayed
29240          * @param {Roo.bootstrap.menu.Menu} this
29241          */
29242         show : true,
29243         /**
29244          * @event hide
29245          * Fires after this menu is hidden
29246          * @param {Roo.bootstrap.menu.Menu} this
29247          */
29248         hide : true,
29249         /**
29250          * @event click
29251          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29252          * @param {Roo.bootstrap.menu.Menu} this
29253          * @param {Roo.EventObject} e
29254          */
29255         click : true
29256     });
29257     
29258 };
29259
29260 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29261     
29262     submenu : false,
29263     html : '',
29264     weight : 'default',
29265     icon : false,
29266     pos : 'bottom',
29267     
29268     
29269     getChildContainer : function() {
29270         if(this.isSubMenu){
29271             return this.el;
29272         }
29273         
29274         return this.el.select('ul.dropdown-menu', true).first();  
29275     },
29276     
29277     getAutoCreate : function()
29278     {
29279         var text = [
29280             {
29281                 tag : 'span',
29282                 cls : 'roo-menu-text',
29283                 html : this.html
29284             }
29285         ];
29286         
29287         if(this.icon){
29288             text.unshift({
29289                 tag : 'i',
29290                 cls : 'fa ' + this.icon
29291             })
29292         }
29293         
29294         
29295         var cfg = {
29296             tag : 'div',
29297             cls : 'btn-group',
29298             cn : [
29299                 {
29300                     tag : 'button',
29301                     cls : 'dropdown-button btn btn-' + this.weight,
29302                     cn : text
29303                 },
29304                 {
29305                     tag : 'button',
29306                     cls : 'dropdown-toggle btn btn-' + this.weight,
29307                     cn : [
29308                         {
29309                             tag : 'span',
29310                             cls : 'caret'
29311                         }
29312                     ]
29313                 },
29314                 {
29315                     tag : 'ul',
29316                     cls : 'dropdown-menu'
29317                 }
29318             ]
29319             
29320         };
29321         
29322         if(this.pos == 'top'){
29323             cfg.cls += ' dropup';
29324         }
29325         
29326         if(this.isSubMenu){
29327             cfg = {
29328                 tag : 'ul',
29329                 cls : 'dropdown-menu'
29330             }
29331         }
29332         
29333         return cfg;
29334     },
29335     
29336     onRender : function(ct, position)
29337     {
29338         this.isSubMenu = ct.hasClass('dropdown-submenu');
29339         
29340         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29341     },
29342     
29343     initEvents : function() 
29344     {
29345         if(this.isSubMenu){
29346             return;
29347         }
29348         
29349         this.hidden = true;
29350         
29351         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29352         this.triggerEl.on('click', this.onTriggerPress, this);
29353         
29354         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29355         this.buttonEl.on('click', this.onClick, this);
29356         
29357     },
29358     
29359     list : function()
29360     {
29361         if(this.isSubMenu){
29362             return this.el;
29363         }
29364         
29365         return this.el.select('ul.dropdown-menu', true).first();
29366     },
29367     
29368     onClick : function(e)
29369     {
29370         this.fireEvent("click", this, e);
29371     },
29372     
29373     onTriggerPress  : function(e)
29374     {   
29375         if (this.isVisible()) {
29376             this.hide();
29377         } else {
29378             this.show();
29379         }
29380     },
29381     
29382     isVisible : function(){
29383         return !this.hidden;
29384     },
29385     
29386     show : function()
29387     {
29388         this.fireEvent("beforeshow", this);
29389         
29390         this.hidden = false;
29391         this.el.addClass('open');
29392         
29393         Roo.get(document).on("mouseup", this.onMouseUp, this);
29394         
29395         this.fireEvent("show", this);
29396         
29397         
29398     },
29399     
29400     hide : function()
29401     {
29402         this.fireEvent("beforehide", this);
29403         
29404         this.hidden = true;
29405         this.el.removeClass('open');
29406         
29407         Roo.get(document).un("mouseup", this.onMouseUp);
29408         
29409         this.fireEvent("hide", this);
29410     },
29411     
29412     onMouseUp : function()
29413     {
29414         this.hide();
29415     }
29416     
29417 });
29418
29419  
29420  /*
29421  * - LGPL
29422  *
29423  * menu item
29424  * 
29425  */
29426 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29427
29428 /**
29429  * @class Roo.bootstrap.menu.Item
29430  * @extends Roo.bootstrap.Component
29431  * Bootstrap MenuItem class
29432  * @cfg {Boolean} submenu (true | false) default false
29433  * @cfg {String} html text of the item
29434  * @cfg {String} href the link
29435  * @cfg {Boolean} disable (true | false) default false
29436  * @cfg {Boolean} preventDefault (true | false) default true
29437  * @cfg {String} icon Font awesome icon
29438  * @cfg {String} pos Submenu align to (left | right) default right 
29439  * 
29440  * 
29441  * @constructor
29442  * Create a new Item
29443  * @param {Object} config The config object
29444  */
29445
29446
29447 Roo.bootstrap.menu.Item = function(config){
29448     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29449     this.addEvents({
29450         /**
29451          * @event mouseover
29452          * Fires when the mouse is hovering over this menu
29453          * @param {Roo.bootstrap.menu.Item} this
29454          * @param {Roo.EventObject} e
29455          */
29456         mouseover : true,
29457         /**
29458          * @event mouseout
29459          * Fires when the mouse exits this menu
29460          * @param {Roo.bootstrap.menu.Item} this
29461          * @param {Roo.EventObject} e
29462          */
29463         mouseout : true,
29464         // raw events
29465         /**
29466          * @event click
29467          * The raw click event for the entire grid.
29468          * @param {Roo.EventObject} e
29469          */
29470         click : true
29471     });
29472 };
29473
29474 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29475     
29476     submenu : false,
29477     href : '',
29478     html : '',
29479     preventDefault: true,
29480     disable : false,
29481     icon : false,
29482     pos : 'right',
29483     
29484     getAutoCreate : function()
29485     {
29486         var text = [
29487             {
29488                 tag : 'span',
29489                 cls : 'roo-menu-item-text',
29490                 html : this.html
29491             }
29492         ];
29493         
29494         if(this.icon){
29495             text.unshift({
29496                 tag : 'i',
29497                 cls : 'fa ' + this.icon
29498             })
29499         }
29500         
29501         var cfg = {
29502             tag : 'li',
29503             cn : [
29504                 {
29505                     tag : 'a',
29506                     href : this.href || '#',
29507                     cn : text
29508                 }
29509             ]
29510         };
29511         
29512         if(this.disable){
29513             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29514         }
29515         
29516         if(this.submenu){
29517             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29518             
29519             if(this.pos == 'left'){
29520                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29521             }
29522         }
29523         
29524         return cfg;
29525     },
29526     
29527     initEvents : function() 
29528     {
29529         this.el.on('mouseover', this.onMouseOver, this);
29530         this.el.on('mouseout', this.onMouseOut, this);
29531         
29532         this.el.select('a', true).first().on('click', this.onClick, this);
29533         
29534     },
29535     
29536     onClick : function(e)
29537     {
29538         if(this.preventDefault){
29539             e.preventDefault();
29540         }
29541         
29542         this.fireEvent("click", this, e);
29543     },
29544     
29545     onMouseOver : function(e)
29546     {
29547         if(this.submenu && this.pos == 'left'){
29548             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29549         }
29550         
29551         this.fireEvent("mouseover", this, e);
29552     },
29553     
29554     onMouseOut : function(e)
29555     {
29556         this.fireEvent("mouseout", this, e);
29557     }
29558 });
29559
29560  
29561
29562  /*
29563  * - LGPL
29564  *
29565  * menu separator
29566  * 
29567  */
29568 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29569
29570 /**
29571  * @class Roo.bootstrap.menu.Separator
29572  * @extends Roo.bootstrap.Component
29573  * Bootstrap Separator class
29574  * 
29575  * @constructor
29576  * Create a new Separator
29577  * @param {Object} config The config object
29578  */
29579
29580
29581 Roo.bootstrap.menu.Separator = function(config){
29582     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29583 };
29584
29585 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29586     
29587     getAutoCreate : function(){
29588         var cfg = {
29589             tag : 'li',
29590             cls: 'dropdown-divider divider'
29591         };
29592         
29593         return cfg;
29594     }
29595    
29596 });
29597
29598  
29599
29600  /*
29601  * - LGPL
29602  *
29603  * Tooltip
29604  * 
29605  */
29606
29607 /**
29608  * @class Roo.bootstrap.Tooltip
29609  * Bootstrap Tooltip class
29610  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29611  * to determine which dom element triggers the tooltip.
29612  * 
29613  * It needs to add support for additional attributes like tooltip-position
29614  * 
29615  * @constructor
29616  * Create a new Toolti
29617  * @param {Object} config The config object
29618  */
29619
29620 Roo.bootstrap.Tooltip = function(config){
29621     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29622     
29623     this.alignment = Roo.bootstrap.Tooltip.alignment;
29624     
29625     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29626         this.alignment = config.alignment;
29627     }
29628     
29629 };
29630
29631 Roo.apply(Roo.bootstrap.Tooltip, {
29632     /**
29633      * @function init initialize tooltip monitoring.
29634      * @static
29635      */
29636     currentEl : false,
29637     currentTip : false,
29638     currentRegion : false,
29639     
29640     //  init : delay?
29641     
29642     init : function()
29643     {
29644         Roo.get(document).on('mouseover', this.enter ,this);
29645         Roo.get(document).on('mouseout', this.leave, this);
29646          
29647         
29648         this.currentTip = new Roo.bootstrap.Tooltip();
29649     },
29650     
29651     enter : function(ev)
29652     {
29653         var dom = ev.getTarget();
29654         
29655         //Roo.log(['enter',dom]);
29656         var el = Roo.fly(dom);
29657         if (this.currentEl) {
29658             //Roo.log(dom);
29659             //Roo.log(this.currentEl);
29660             //Roo.log(this.currentEl.contains(dom));
29661             if (this.currentEl == el) {
29662                 return;
29663             }
29664             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29665                 return;
29666             }
29667
29668         }
29669         
29670         if (this.currentTip.el) {
29671             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29672         }    
29673         //Roo.log(ev);
29674         
29675         if(!el || el.dom == document){
29676             return;
29677         }
29678         
29679         var bindEl = el; 
29680         var pel = false;
29681         if (!el.attr('tooltip')) {
29682             pel = el.findParent("[tooltip]");
29683             if (pel) {
29684                 bindEl = Roo.get(pel);
29685             }
29686         }
29687         
29688        
29689         
29690         // you can not look for children, as if el is the body.. then everythign is the child..
29691         if (!pel && !el.attr('tooltip')) { //
29692             if (!el.select("[tooltip]").elements.length) {
29693                 return;
29694             }
29695             // is the mouse over this child...?
29696             bindEl = el.select("[tooltip]").first();
29697             var xy = ev.getXY();
29698             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29699                 //Roo.log("not in region.");
29700                 return;
29701             }
29702             //Roo.log("child element over..");
29703             
29704         }
29705         this.currentEl = el;
29706         this.currentTip.bind(bindEl);
29707         this.currentRegion = Roo.lib.Region.getRegion(dom);
29708         this.currentTip.enter();
29709         
29710     },
29711     leave : function(ev)
29712     {
29713         var dom = ev.getTarget();
29714         //Roo.log(['leave',dom]);
29715         if (!this.currentEl) {
29716             return;
29717         }
29718         
29719         
29720         if (dom != this.currentEl.dom) {
29721             return;
29722         }
29723         var xy = ev.getXY();
29724         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29725             return;
29726         }
29727         // only activate leave if mouse cursor is outside... bounding box..
29728         
29729         
29730         
29731         
29732         if (this.currentTip) {
29733             this.currentTip.leave();
29734         }
29735         //Roo.log('clear currentEl');
29736         this.currentEl = false;
29737         
29738         
29739     },
29740     alignment : {
29741         'left' : ['r-l', [-2,0], 'right'],
29742         'right' : ['l-r', [2,0], 'left'],
29743         'bottom' : ['t-b', [0,2], 'top'],
29744         'top' : [ 'b-t', [0,-2], 'bottom']
29745     }
29746     
29747 });
29748
29749
29750 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29751     
29752     
29753     bindEl : false,
29754     
29755     delay : null, // can be { show : 300 , hide: 500}
29756     
29757     timeout : null,
29758     
29759     hoverState : null, //???
29760     
29761     placement : 'bottom', 
29762     
29763     alignment : false,
29764     
29765     getAutoCreate : function(){
29766     
29767         var cfg = {
29768            cls : 'tooltip',   
29769            role : 'tooltip',
29770            cn : [
29771                 {
29772                     cls : 'tooltip-arrow arrow'
29773                 },
29774                 {
29775                     cls : 'tooltip-inner'
29776                 }
29777            ]
29778         };
29779         
29780         return cfg;
29781     },
29782     bind : function(el)
29783     {
29784         this.bindEl = el;
29785     },
29786     
29787     initEvents : function()
29788     {
29789         this.arrowEl = this.el.select('.arrow', true).first();
29790         this.innerEl = this.el.select('.tooltip-inner', true).first();
29791     },
29792     
29793     enter : function () {
29794        
29795         if (this.timeout != null) {
29796             clearTimeout(this.timeout);
29797         }
29798         
29799         this.hoverState = 'in';
29800          //Roo.log("enter - show");
29801         if (!this.delay || !this.delay.show) {
29802             this.show();
29803             return;
29804         }
29805         var _t = this;
29806         this.timeout = setTimeout(function () {
29807             if (_t.hoverState == 'in') {
29808                 _t.show();
29809             }
29810         }, this.delay.show);
29811     },
29812     leave : function()
29813     {
29814         clearTimeout(this.timeout);
29815     
29816         this.hoverState = 'out';
29817          if (!this.delay || !this.delay.hide) {
29818             this.hide();
29819             return;
29820         }
29821        
29822         var _t = this;
29823         this.timeout = setTimeout(function () {
29824             //Roo.log("leave - timeout");
29825             
29826             if (_t.hoverState == 'out') {
29827                 _t.hide();
29828                 Roo.bootstrap.Tooltip.currentEl = false;
29829             }
29830         }, delay);
29831     },
29832     
29833     show : function (msg)
29834     {
29835         if (!this.el) {
29836             this.render(document.body);
29837         }
29838         // set content.
29839         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29840         
29841         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29842         
29843         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29844         
29845         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29846                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29847         
29848         var placement = typeof this.placement == 'function' ?
29849             this.placement.call(this, this.el, on_el) :
29850             this.placement;
29851             
29852         var autoToken = /\s?auto?\s?/i;
29853         var autoPlace = autoToken.test(placement);
29854         if (autoPlace) {
29855             placement = placement.replace(autoToken, '') || 'top';
29856         }
29857         
29858         //this.el.detach()
29859         //this.el.setXY([0,0]);
29860         this.el.show();
29861         //this.el.dom.style.display='block';
29862         
29863         //this.el.appendTo(on_el);
29864         
29865         var p = this.getPosition();
29866         var box = this.el.getBox();
29867         
29868         if (autoPlace) {
29869             // fixme..
29870         }
29871         
29872         var align = this.alignment[placement];
29873         
29874         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29875         
29876         if(placement == 'top' || placement == 'bottom'){
29877             if(xy[0] < 0){
29878                 placement = 'right';
29879             }
29880             
29881             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29882                 placement = 'left';
29883             }
29884             
29885             var scroll = Roo.select('body', true).first().getScroll();
29886             
29887             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29888                 placement = 'top';
29889             }
29890             
29891             align = this.alignment[placement];
29892             
29893             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29894             
29895         }
29896         
29897         var elems = document.getElementsByTagName('div');
29898         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29899         for (var i = 0; i < elems.length; i++) {
29900           var zindex = Number.parseInt(
29901                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29902                 10
29903           );
29904           if (zindex > highest) {
29905             highest = zindex;
29906           }
29907         }
29908         
29909         
29910         
29911         this.el.dom.style.zIndex = highest;
29912         
29913         this.el.alignTo(this.bindEl, align[0],align[1]);
29914         //var arrow = this.el.select('.arrow',true).first();
29915         //arrow.set(align[2], 
29916         
29917         this.el.addClass(placement);
29918         this.el.addClass("bs-tooltip-"+ placement);
29919         
29920         this.el.addClass('in fade show');
29921         
29922         this.hoverState = null;
29923         
29924         if (this.el.hasClass('fade')) {
29925             // fade it?
29926         }
29927         
29928         
29929         
29930         
29931         
29932     },
29933     hide : function()
29934     {
29935          
29936         if (!this.el) {
29937             return;
29938         }
29939         //this.el.setXY([0,0]);
29940         this.el.removeClass(['show', 'in']);
29941         //this.el.hide();
29942         
29943     }
29944     
29945 });
29946  
29947
29948  /*
29949  * - LGPL
29950  *
29951  * Location Picker
29952  * 
29953  */
29954
29955 /**
29956  * @class Roo.bootstrap.LocationPicker
29957  * @extends Roo.bootstrap.Component
29958  * Bootstrap LocationPicker class
29959  * @cfg {Number} latitude Position when init default 0
29960  * @cfg {Number} longitude Position when init default 0
29961  * @cfg {Number} zoom default 15
29962  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29963  * @cfg {Boolean} mapTypeControl default false
29964  * @cfg {Boolean} disableDoubleClickZoom default false
29965  * @cfg {Boolean} scrollwheel default true
29966  * @cfg {Boolean} streetViewControl default false
29967  * @cfg {Number} radius default 0
29968  * @cfg {String} locationName
29969  * @cfg {Boolean} draggable default true
29970  * @cfg {Boolean} enableAutocomplete default false
29971  * @cfg {Boolean} enableReverseGeocode default true
29972  * @cfg {String} markerTitle
29973  * 
29974  * @constructor
29975  * Create a new LocationPicker
29976  * @param {Object} config The config object
29977  */
29978
29979
29980 Roo.bootstrap.LocationPicker = function(config){
29981     
29982     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29983     
29984     this.addEvents({
29985         /**
29986          * @event initial
29987          * Fires when the picker initialized.
29988          * @param {Roo.bootstrap.LocationPicker} this
29989          * @param {Google Location} location
29990          */
29991         initial : true,
29992         /**
29993          * @event positionchanged
29994          * Fires when the picker position changed.
29995          * @param {Roo.bootstrap.LocationPicker} this
29996          * @param {Google Location} location
29997          */
29998         positionchanged : true,
29999         /**
30000          * @event resize
30001          * Fires when the map resize.
30002          * @param {Roo.bootstrap.LocationPicker} this
30003          */
30004         resize : true,
30005         /**
30006          * @event show
30007          * Fires when the map show.
30008          * @param {Roo.bootstrap.LocationPicker} this
30009          */
30010         show : true,
30011         /**
30012          * @event hide
30013          * Fires when the map hide.
30014          * @param {Roo.bootstrap.LocationPicker} this
30015          */
30016         hide : true,
30017         /**
30018          * @event mapClick
30019          * Fires when click the map.
30020          * @param {Roo.bootstrap.LocationPicker} this
30021          * @param {Map event} e
30022          */
30023         mapClick : true,
30024         /**
30025          * @event mapRightClick
30026          * Fires when right click the map.
30027          * @param {Roo.bootstrap.LocationPicker} this
30028          * @param {Map event} e
30029          */
30030         mapRightClick : true,
30031         /**
30032          * @event markerClick
30033          * Fires when click the marker.
30034          * @param {Roo.bootstrap.LocationPicker} this
30035          * @param {Map event} e
30036          */
30037         markerClick : true,
30038         /**
30039          * @event markerRightClick
30040          * Fires when right click the marker.
30041          * @param {Roo.bootstrap.LocationPicker} this
30042          * @param {Map event} e
30043          */
30044         markerRightClick : true,
30045         /**
30046          * @event OverlayViewDraw
30047          * Fires when OverlayView Draw
30048          * @param {Roo.bootstrap.LocationPicker} this
30049          */
30050         OverlayViewDraw : true,
30051         /**
30052          * @event OverlayViewOnAdd
30053          * Fires when OverlayView Draw
30054          * @param {Roo.bootstrap.LocationPicker} this
30055          */
30056         OverlayViewOnAdd : true,
30057         /**
30058          * @event OverlayViewOnRemove
30059          * Fires when OverlayView Draw
30060          * @param {Roo.bootstrap.LocationPicker} this
30061          */
30062         OverlayViewOnRemove : true,
30063         /**
30064          * @event OverlayViewShow
30065          * Fires when OverlayView Draw
30066          * @param {Roo.bootstrap.LocationPicker} this
30067          * @param {Pixel} cpx
30068          */
30069         OverlayViewShow : true,
30070         /**
30071          * @event OverlayViewHide
30072          * Fires when OverlayView Draw
30073          * @param {Roo.bootstrap.LocationPicker} this
30074          */
30075         OverlayViewHide : true,
30076         /**
30077          * @event loadexception
30078          * Fires when load google lib failed.
30079          * @param {Roo.bootstrap.LocationPicker} this
30080          */
30081         loadexception : true
30082     });
30083         
30084 };
30085
30086 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30087     
30088     gMapContext: false,
30089     
30090     latitude: 0,
30091     longitude: 0,
30092     zoom: 15,
30093     mapTypeId: false,
30094     mapTypeControl: false,
30095     disableDoubleClickZoom: false,
30096     scrollwheel: true,
30097     streetViewControl: false,
30098     radius: 0,
30099     locationName: '',
30100     draggable: true,
30101     enableAutocomplete: false,
30102     enableReverseGeocode: true,
30103     markerTitle: '',
30104     
30105     getAutoCreate: function()
30106     {
30107
30108         var cfg = {
30109             tag: 'div',
30110             cls: 'roo-location-picker'
30111         };
30112         
30113         return cfg
30114     },
30115     
30116     initEvents: function(ct, position)
30117     {       
30118         if(!this.el.getWidth() || this.isApplied()){
30119             return;
30120         }
30121         
30122         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30123         
30124         this.initial();
30125     },
30126     
30127     initial: function()
30128     {
30129         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30130             this.fireEvent('loadexception', this);
30131             return;
30132         }
30133         
30134         if(!this.mapTypeId){
30135             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30136         }
30137         
30138         this.gMapContext = this.GMapContext();
30139         
30140         this.initOverlayView();
30141         
30142         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30143         
30144         var _this = this;
30145                 
30146         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30147             _this.setPosition(_this.gMapContext.marker.position);
30148         });
30149         
30150         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30151             _this.fireEvent('mapClick', this, event);
30152             
30153         });
30154
30155         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30156             _this.fireEvent('mapRightClick', this, event);
30157             
30158         });
30159         
30160         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30161             _this.fireEvent('markerClick', this, event);
30162             
30163         });
30164
30165         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30166             _this.fireEvent('markerRightClick', this, event);
30167             
30168         });
30169         
30170         this.setPosition(this.gMapContext.location);
30171         
30172         this.fireEvent('initial', this, this.gMapContext.location);
30173     },
30174     
30175     initOverlayView: function()
30176     {
30177         var _this = this;
30178         
30179         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30180             
30181             draw: function()
30182             {
30183                 _this.fireEvent('OverlayViewDraw', _this);
30184             },
30185             
30186             onAdd: function()
30187             {
30188                 _this.fireEvent('OverlayViewOnAdd', _this);
30189             },
30190             
30191             onRemove: function()
30192             {
30193                 _this.fireEvent('OverlayViewOnRemove', _this);
30194             },
30195             
30196             show: function(cpx)
30197             {
30198                 _this.fireEvent('OverlayViewShow', _this, cpx);
30199             },
30200             
30201             hide: function()
30202             {
30203                 _this.fireEvent('OverlayViewHide', _this);
30204             }
30205             
30206         });
30207     },
30208     
30209     fromLatLngToContainerPixel: function(event)
30210     {
30211         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30212     },
30213     
30214     isApplied: function() 
30215     {
30216         return this.getGmapContext() == false ? false : true;
30217     },
30218     
30219     getGmapContext: function() 
30220     {
30221         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30222     },
30223     
30224     GMapContext: function() 
30225     {
30226         var position = new google.maps.LatLng(this.latitude, this.longitude);
30227         
30228         var _map = new google.maps.Map(this.el.dom, {
30229             center: position,
30230             zoom: this.zoom,
30231             mapTypeId: this.mapTypeId,
30232             mapTypeControl: this.mapTypeControl,
30233             disableDoubleClickZoom: this.disableDoubleClickZoom,
30234             scrollwheel: this.scrollwheel,
30235             streetViewControl: this.streetViewControl,
30236             locationName: this.locationName,
30237             draggable: this.draggable,
30238             enableAutocomplete: this.enableAutocomplete,
30239             enableReverseGeocode: this.enableReverseGeocode
30240         });
30241         
30242         var _marker = new google.maps.Marker({
30243             position: position,
30244             map: _map,
30245             title: this.markerTitle,
30246             draggable: this.draggable
30247         });
30248         
30249         return {
30250             map: _map,
30251             marker: _marker,
30252             circle: null,
30253             location: position,
30254             radius: this.radius,
30255             locationName: this.locationName,
30256             addressComponents: {
30257                 formatted_address: null,
30258                 addressLine1: null,
30259                 addressLine2: null,
30260                 streetName: null,
30261                 streetNumber: null,
30262                 city: null,
30263                 district: null,
30264                 state: null,
30265                 stateOrProvince: null
30266             },
30267             settings: this,
30268             domContainer: this.el.dom,
30269             geodecoder: new google.maps.Geocoder()
30270         };
30271     },
30272     
30273     drawCircle: function(center, radius, options) 
30274     {
30275         if (this.gMapContext.circle != null) {
30276             this.gMapContext.circle.setMap(null);
30277         }
30278         if (radius > 0) {
30279             radius *= 1;
30280             options = Roo.apply({}, options, {
30281                 strokeColor: "#0000FF",
30282                 strokeOpacity: .35,
30283                 strokeWeight: 2,
30284                 fillColor: "#0000FF",
30285                 fillOpacity: .2
30286             });
30287             
30288             options.map = this.gMapContext.map;
30289             options.radius = radius;
30290             options.center = center;
30291             this.gMapContext.circle = new google.maps.Circle(options);
30292             return this.gMapContext.circle;
30293         }
30294         
30295         return null;
30296     },
30297     
30298     setPosition: function(location) 
30299     {
30300         this.gMapContext.location = location;
30301         this.gMapContext.marker.setPosition(location);
30302         this.gMapContext.map.panTo(location);
30303         this.drawCircle(location, this.gMapContext.radius, {});
30304         
30305         var _this = this;
30306         
30307         if (this.gMapContext.settings.enableReverseGeocode) {
30308             this.gMapContext.geodecoder.geocode({
30309                 latLng: this.gMapContext.location
30310             }, function(results, status) {
30311                 
30312                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30313                     _this.gMapContext.locationName = results[0].formatted_address;
30314                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30315                     
30316                     _this.fireEvent('positionchanged', this, location);
30317                 }
30318             });
30319             
30320             return;
30321         }
30322         
30323         this.fireEvent('positionchanged', this, location);
30324     },
30325     
30326     resize: function()
30327     {
30328         google.maps.event.trigger(this.gMapContext.map, "resize");
30329         
30330         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30331         
30332         this.fireEvent('resize', this);
30333     },
30334     
30335     setPositionByLatLng: function(latitude, longitude)
30336     {
30337         this.setPosition(new google.maps.LatLng(latitude, longitude));
30338     },
30339     
30340     getCurrentPosition: function() 
30341     {
30342         return {
30343             latitude: this.gMapContext.location.lat(),
30344             longitude: this.gMapContext.location.lng()
30345         };
30346     },
30347     
30348     getAddressName: function() 
30349     {
30350         return this.gMapContext.locationName;
30351     },
30352     
30353     getAddressComponents: function() 
30354     {
30355         return this.gMapContext.addressComponents;
30356     },
30357     
30358     address_component_from_google_geocode: function(address_components) 
30359     {
30360         var result = {};
30361         
30362         for (var i = 0; i < address_components.length; i++) {
30363             var component = address_components[i];
30364             if (component.types.indexOf("postal_code") >= 0) {
30365                 result.postalCode = component.short_name;
30366             } else if (component.types.indexOf("street_number") >= 0) {
30367                 result.streetNumber = component.short_name;
30368             } else if (component.types.indexOf("route") >= 0) {
30369                 result.streetName = component.short_name;
30370             } else if (component.types.indexOf("neighborhood") >= 0) {
30371                 result.city = component.short_name;
30372             } else if (component.types.indexOf("locality") >= 0) {
30373                 result.city = component.short_name;
30374             } else if (component.types.indexOf("sublocality") >= 0) {
30375                 result.district = component.short_name;
30376             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30377                 result.stateOrProvince = component.short_name;
30378             } else if (component.types.indexOf("country") >= 0) {
30379                 result.country = component.short_name;
30380             }
30381         }
30382         
30383         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30384         result.addressLine2 = "";
30385         return result;
30386     },
30387     
30388     setZoomLevel: function(zoom)
30389     {
30390         this.gMapContext.map.setZoom(zoom);
30391     },
30392     
30393     show: function()
30394     {
30395         if(!this.el){
30396             return;
30397         }
30398         
30399         this.el.show();
30400         
30401         this.resize();
30402         
30403         this.fireEvent('show', this);
30404     },
30405     
30406     hide: function()
30407     {
30408         if(!this.el){
30409             return;
30410         }
30411         
30412         this.el.hide();
30413         
30414         this.fireEvent('hide', this);
30415     }
30416     
30417 });
30418
30419 Roo.apply(Roo.bootstrap.LocationPicker, {
30420     
30421     OverlayView : function(map, options)
30422     {
30423         options = options || {};
30424         
30425         this.setMap(map);
30426     }
30427     
30428     
30429 });/**
30430  * @class Roo.bootstrap.Alert
30431  * @extends Roo.bootstrap.Component
30432  * Bootstrap Alert class - shows an alert area box
30433  * eg
30434  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30435   Enter a valid email address
30436 </div>
30437  * @licence LGPL
30438  * @cfg {String} title The title of alert
30439  * @cfg {String} html The content of alert
30440  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30441  * @cfg {String} fa font-awesomeicon
30442  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30443  * @cfg {Boolean} close true to show a x closer
30444  * 
30445  * 
30446  * @constructor
30447  * Create a new alert
30448  * @param {Object} config The config object
30449  */
30450
30451
30452 Roo.bootstrap.Alert = function(config){
30453     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30454     
30455 };
30456
30457 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30458     
30459     title: '',
30460     html: '',
30461     weight: false,
30462     fa: false,
30463     faicon: false, // BC
30464     close : false,
30465     
30466     
30467     getAutoCreate : function()
30468     {
30469         
30470         var cfg = {
30471             tag : 'div',
30472             cls : 'alert',
30473             cn : [
30474                 {
30475                     tag: 'button',
30476                     type :  "button",
30477                     cls: "close",
30478                     html : '×',
30479                     style : this.close ? '' : 'display:none'
30480                 },
30481                 {
30482                     tag : 'i',
30483                     cls : 'roo-alert-icon'
30484                     
30485                 },
30486                 {
30487                     tag : 'b',
30488                     cls : 'roo-alert-title',
30489                     html : this.title
30490                 },
30491                 {
30492                     tag : 'span',
30493                     cls : 'roo-alert-text',
30494                     html : this.html
30495                 }
30496             ]
30497         };
30498         
30499         if(this.faicon){
30500             cfg.cn[0].cls += ' fa ' + this.faicon;
30501         }
30502         if(this.fa){
30503             cfg.cn[0].cls += ' fa ' + this.fa;
30504         }
30505         
30506         if(this.weight){
30507             cfg.cls += ' alert-' + this.weight;
30508         }
30509         
30510         return cfg;
30511     },
30512     
30513     initEvents: function() 
30514     {
30515         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30516         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30517         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30518         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30519         if (this.seconds > 0) {
30520             this.hide.defer(this.seconds, this);
30521         }
30522     },
30523     /**
30524      * Set the Title Message HTML
30525      * @param {String} html
30526      */
30527     setTitle : function(str)
30528     {
30529         this.titleEl.dom.innerHTML = str;
30530     },
30531      
30532      /**
30533      * Set the Body Message HTML
30534      * @param {String} html
30535      */
30536     setHtml : function(str)
30537     {
30538         this.htmlEl.dom.innerHTML = str;
30539     },
30540     /**
30541      * Set the Weight of the alert
30542      * @param {String} (success|info|warning|danger) weight
30543      */
30544     
30545     setWeight : function(weight)
30546     {
30547         if(this.weight){
30548             this.el.removeClass('alert-' + this.weight);
30549         }
30550         
30551         this.weight = weight;
30552         
30553         this.el.addClass('alert-' + this.weight);
30554     },
30555       /**
30556      * Set the Icon of the alert
30557      * @param {String} see fontawsome names (name without the 'fa-' bit)
30558      */
30559     setIcon : function(icon)
30560     {
30561         if(this.faicon){
30562             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30563         }
30564         
30565         this.faicon = icon;
30566         
30567         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30568     },
30569     /**
30570      * Hide the Alert
30571      */
30572     hide: function() 
30573     {
30574         this.el.hide();   
30575     },
30576     /**
30577      * Show the Alert
30578      */
30579     show: function() 
30580     {  
30581         this.el.show();   
30582     }
30583     
30584 });
30585
30586  
30587 /*
30588 * Licence: LGPL
30589 */
30590
30591 /**
30592  * @class Roo.bootstrap.UploadCropbox
30593  * @extends Roo.bootstrap.Component
30594  * Bootstrap UploadCropbox class
30595  * @cfg {String} emptyText show when image has been loaded
30596  * @cfg {String} rotateNotify show when image too small to rotate
30597  * @cfg {Number} errorTimeout default 3000
30598  * @cfg {Number} minWidth default 300
30599  * @cfg {Number} minHeight default 300
30600  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30601  * @cfg {Boolean} isDocument (true|false) default false
30602  * @cfg {String} url action url
30603  * @cfg {String} paramName default 'imageUpload'
30604  * @cfg {String} method default POST
30605  * @cfg {Boolean} loadMask (true|false) default true
30606  * @cfg {Boolean} loadingText default 'Loading...'
30607  * 
30608  * @constructor
30609  * Create a new UploadCropbox
30610  * @param {Object} config The config object
30611  */
30612
30613 Roo.bootstrap.UploadCropbox = function(config){
30614     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30615     
30616     this.addEvents({
30617         /**
30618          * @event beforeselectfile
30619          * Fire before select file
30620          * @param {Roo.bootstrap.UploadCropbox} this
30621          */
30622         "beforeselectfile" : true,
30623         /**
30624          * @event initial
30625          * Fire after initEvent
30626          * @param {Roo.bootstrap.UploadCropbox} this
30627          */
30628         "initial" : true,
30629         /**
30630          * @event crop
30631          * Fire after initEvent
30632          * @param {Roo.bootstrap.UploadCropbox} this
30633          * @param {String} data
30634          */
30635         "crop" : true,
30636         /**
30637          * @event prepare
30638          * Fire when preparing the file data
30639          * @param {Roo.bootstrap.UploadCropbox} this
30640          * @param {Object} file
30641          */
30642         "prepare" : true,
30643         /**
30644          * @event exception
30645          * Fire when get exception
30646          * @param {Roo.bootstrap.UploadCropbox} this
30647          * @param {XMLHttpRequest} xhr
30648          */
30649         "exception" : true,
30650         /**
30651          * @event beforeloadcanvas
30652          * Fire before load the canvas
30653          * @param {Roo.bootstrap.UploadCropbox} this
30654          * @param {String} src
30655          */
30656         "beforeloadcanvas" : true,
30657         /**
30658          * @event trash
30659          * Fire when trash image
30660          * @param {Roo.bootstrap.UploadCropbox} this
30661          */
30662         "trash" : true,
30663         /**
30664          * @event download
30665          * Fire when download the image
30666          * @param {Roo.bootstrap.UploadCropbox} this
30667          */
30668         "download" : true,
30669         /**
30670          * @event footerbuttonclick
30671          * Fire when footerbuttonclick
30672          * @param {Roo.bootstrap.UploadCropbox} this
30673          * @param {String} type
30674          */
30675         "footerbuttonclick" : true,
30676         /**
30677          * @event resize
30678          * Fire when resize
30679          * @param {Roo.bootstrap.UploadCropbox} this
30680          */
30681         "resize" : true,
30682         /**
30683          * @event rotate
30684          * Fire when rotate the image
30685          * @param {Roo.bootstrap.UploadCropbox} this
30686          * @param {String} pos
30687          */
30688         "rotate" : true,
30689         /**
30690          * @event inspect
30691          * Fire when inspect the file
30692          * @param {Roo.bootstrap.UploadCropbox} this
30693          * @param {Object} file
30694          */
30695         "inspect" : true,
30696         /**
30697          * @event upload
30698          * Fire when xhr upload the file
30699          * @param {Roo.bootstrap.UploadCropbox} this
30700          * @param {Object} data
30701          */
30702         "upload" : true,
30703         /**
30704          * @event arrange
30705          * Fire when arrange the file data
30706          * @param {Roo.bootstrap.UploadCropbox} this
30707          * @param {Object} formData
30708          */
30709         "arrange" : true
30710     });
30711     
30712     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30713 };
30714
30715 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30716     
30717     emptyText : 'Click to upload image',
30718     rotateNotify : 'Image is too small to rotate',
30719     errorTimeout : 3000,
30720     scale : 0,
30721     baseScale : 1,
30722     rotate : 0,
30723     dragable : false,
30724     pinching : false,
30725     mouseX : 0,
30726     mouseY : 0,
30727     cropData : false,
30728     minWidth : 300,
30729     minHeight : 300,
30730     file : false,
30731     exif : {},
30732     baseRotate : 1,
30733     cropType : 'image/jpeg',
30734     buttons : false,
30735     canvasLoaded : false,
30736     isDocument : false,
30737     method : 'POST',
30738     paramName : 'imageUpload',
30739     loadMask : true,
30740     loadingText : 'Loading...',
30741     maskEl : false,
30742     
30743     getAutoCreate : function()
30744     {
30745         var cfg = {
30746             tag : 'div',
30747             cls : 'roo-upload-cropbox',
30748             cn : [
30749                 {
30750                     tag : 'input',
30751                     cls : 'roo-upload-cropbox-selector',
30752                     type : 'file'
30753                 },
30754                 {
30755                     tag : 'div',
30756                     cls : 'roo-upload-cropbox-body',
30757                     style : 'cursor:pointer',
30758                     cn : [
30759                         {
30760                             tag : 'div',
30761                             cls : 'roo-upload-cropbox-preview'
30762                         },
30763                         {
30764                             tag : 'div',
30765                             cls : 'roo-upload-cropbox-thumb'
30766                         },
30767                         {
30768                             tag : 'div',
30769                             cls : 'roo-upload-cropbox-empty-notify',
30770                             html : this.emptyText
30771                         },
30772                         {
30773                             tag : 'div',
30774                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30775                             html : this.rotateNotify
30776                         }
30777                     ]
30778                 },
30779                 {
30780                     tag : 'div',
30781                     cls : 'roo-upload-cropbox-footer',
30782                     cn : {
30783                         tag : 'div',
30784                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30785                         cn : []
30786                     }
30787                 }
30788             ]
30789         };
30790         
30791         return cfg;
30792     },
30793     
30794     onRender : function(ct, position)
30795     {
30796         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30797         
30798         if (this.buttons.length) {
30799             
30800             Roo.each(this.buttons, function(bb) {
30801                 
30802                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30803                 
30804                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30805                 
30806             }, this);
30807         }
30808         
30809         if(this.loadMask){
30810             this.maskEl = this.el;
30811         }
30812     },
30813     
30814     initEvents : function()
30815     {
30816         this.urlAPI = (window.createObjectURL && window) || 
30817                                 (window.URL && URL.revokeObjectURL && URL) || 
30818                                 (window.webkitURL && webkitURL);
30819                         
30820         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30821         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30822         
30823         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30824         this.selectorEl.hide();
30825         
30826         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30827         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30828         
30829         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30830         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30831         this.thumbEl.hide();
30832         
30833         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30834         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30835         
30836         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30837         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30838         this.errorEl.hide();
30839         
30840         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30841         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30842         this.footerEl.hide();
30843         
30844         this.setThumbBoxSize();
30845         
30846         this.bind();
30847         
30848         this.resize();
30849         
30850         this.fireEvent('initial', this);
30851     },
30852
30853     bind : function()
30854     {
30855         var _this = this;
30856         
30857         window.addEventListener("resize", function() { _this.resize(); } );
30858         
30859         this.bodyEl.on('click', this.beforeSelectFile, this);
30860         
30861         if(Roo.isTouch){
30862             this.bodyEl.on('touchstart', this.onTouchStart, this);
30863             this.bodyEl.on('touchmove', this.onTouchMove, this);
30864             this.bodyEl.on('touchend', this.onTouchEnd, this);
30865         }
30866         
30867         if(!Roo.isTouch){
30868             this.bodyEl.on('mousedown', this.onMouseDown, this);
30869             this.bodyEl.on('mousemove', this.onMouseMove, this);
30870             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30871             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30872             Roo.get(document).on('mouseup', this.onMouseUp, this);
30873         }
30874         
30875         this.selectorEl.on('change', this.onFileSelected, this);
30876     },
30877     
30878     reset : function()
30879     {    
30880         this.scale = 0;
30881         this.baseScale = 1;
30882         this.rotate = 0;
30883         this.baseRotate = 1;
30884         this.dragable = false;
30885         this.pinching = false;
30886         this.mouseX = 0;
30887         this.mouseY = 0;
30888         this.cropData = false;
30889         this.notifyEl.dom.innerHTML = this.emptyText;
30890         
30891         this.selectorEl.dom.value = '';
30892         
30893     },
30894     
30895     resize : function()
30896     {
30897         if(this.fireEvent('resize', this) != false){
30898             this.setThumbBoxPosition();
30899             this.setCanvasPosition();
30900         }
30901     },
30902     
30903     onFooterButtonClick : function(e, el, o, type)
30904     {
30905         switch (type) {
30906             case 'rotate-left' :
30907                 this.onRotateLeft(e);
30908                 break;
30909             case 'rotate-right' :
30910                 this.onRotateRight(e);
30911                 break;
30912             case 'picture' :
30913                 this.beforeSelectFile(e);
30914                 break;
30915             case 'trash' :
30916                 this.trash(e);
30917                 break;
30918             case 'crop' :
30919                 this.crop(e);
30920                 break;
30921             case 'download' :
30922                 this.download(e);
30923                 break;
30924             default :
30925                 break;
30926         }
30927         
30928         this.fireEvent('footerbuttonclick', this, type);
30929     },
30930     
30931     beforeSelectFile : function(e)
30932     {
30933         e.preventDefault();
30934         
30935         if(this.fireEvent('beforeselectfile', this) != false){
30936             this.selectorEl.dom.click();
30937         }
30938     },
30939     
30940     onFileSelected : function(e)
30941     {
30942         e.preventDefault();
30943         
30944         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30945             return;
30946         }
30947         
30948         var file = this.selectorEl.dom.files[0];
30949         
30950         if(this.fireEvent('inspect', this, file) != false){
30951             this.prepare(file);
30952         }
30953         
30954     },
30955     
30956     trash : function(e)
30957     {
30958         this.fireEvent('trash', this);
30959     },
30960     
30961     download : function(e)
30962     {
30963         this.fireEvent('download', this);
30964     },
30965     
30966     loadCanvas : function(src)
30967     {   
30968         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30969             
30970             this.reset();
30971             
30972             this.imageEl = document.createElement('img');
30973             
30974             var _this = this;
30975             
30976             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30977             
30978             this.imageEl.src = src;
30979         }
30980     },
30981     
30982     onLoadCanvas : function()
30983     {   
30984         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30985         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30986         
30987         this.bodyEl.un('click', this.beforeSelectFile, this);
30988         
30989         this.notifyEl.hide();
30990         this.thumbEl.show();
30991         this.footerEl.show();
30992         
30993         this.baseRotateLevel();
30994         
30995         if(this.isDocument){
30996             this.setThumbBoxSize();
30997         }
30998         
30999         this.setThumbBoxPosition();
31000         
31001         this.baseScaleLevel();
31002         
31003         this.draw();
31004         
31005         this.resize();
31006         
31007         this.canvasLoaded = true;
31008         
31009         if(this.loadMask){
31010             this.maskEl.unmask();
31011         }
31012         
31013     },
31014     
31015     setCanvasPosition : function()
31016     {   
31017         if(!this.canvasEl){
31018             return;
31019         }
31020         
31021         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31022         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31023         
31024         this.previewEl.setLeft(pw);
31025         this.previewEl.setTop(ph);
31026         
31027     },
31028     
31029     onMouseDown : function(e)
31030     {   
31031         e.stopEvent();
31032         
31033         this.dragable = true;
31034         this.pinching = false;
31035         
31036         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31037             this.dragable = false;
31038             return;
31039         }
31040         
31041         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31042         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31043         
31044     },
31045     
31046     onMouseMove : function(e)
31047     {   
31048         e.stopEvent();
31049         
31050         if(!this.canvasLoaded){
31051             return;
31052         }
31053         
31054         if (!this.dragable){
31055             return;
31056         }
31057         
31058         var minX = Math.ceil(this.thumbEl.getLeft(true));
31059         var minY = Math.ceil(this.thumbEl.getTop(true));
31060         
31061         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31062         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31063         
31064         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31065         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31066         
31067         x = x - this.mouseX;
31068         y = y - this.mouseY;
31069         
31070         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31071         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31072         
31073         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31074         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31075         
31076         this.previewEl.setLeft(bgX);
31077         this.previewEl.setTop(bgY);
31078         
31079         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31080         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31081     },
31082     
31083     onMouseUp : function(e)
31084     {   
31085         e.stopEvent();
31086         
31087         this.dragable = false;
31088     },
31089     
31090     onMouseWheel : function(e)
31091     {   
31092         e.stopEvent();
31093         
31094         this.startScale = this.scale;
31095         
31096         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31097         
31098         if(!this.zoomable()){
31099             this.scale = this.startScale;
31100             return;
31101         }
31102         
31103         this.draw();
31104         
31105         return;
31106     },
31107     
31108     zoomable : function()
31109     {
31110         var minScale = this.thumbEl.getWidth() / this.minWidth;
31111         
31112         if(this.minWidth < this.minHeight){
31113             minScale = this.thumbEl.getHeight() / this.minHeight;
31114         }
31115         
31116         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31117         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31118         
31119         if(
31120                 this.isDocument &&
31121                 (this.rotate == 0 || this.rotate == 180) && 
31122                 (
31123                     width > this.imageEl.OriginWidth || 
31124                     height > this.imageEl.OriginHeight ||
31125                     (width < this.minWidth && height < this.minHeight)
31126                 )
31127         ){
31128             return false;
31129         }
31130         
31131         if(
31132                 this.isDocument &&
31133                 (this.rotate == 90 || this.rotate == 270) && 
31134                 (
31135                     width > this.imageEl.OriginWidth || 
31136                     height > this.imageEl.OriginHeight ||
31137                     (width < this.minHeight && height < this.minWidth)
31138                 )
31139         ){
31140             return false;
31141         }
31142         
31143         if(
31144                 !this.isDocument &&
31145                 (this.rotate == 0 || this.rotate == 180) && 
31146                 (
31147                     width < this.minWidth || 
31148                     width > this.imageEl.OriginWidth || 
31149                     height < this.minHeight || 
31150                     height > this.imageEl.OriginHeight
31151                 )
31152         ){
31153             return false;
31154         }
31155         
31156         if(
31157                 !this.isDocument &&
31158                 (this.rotate == 90 || this.rotate == 270) && 
31159                 (
31160                     width < this.minHeight || 
31161                     width > this.imageEl.OriginWidth || 
31162                     height < this.minWidth || 
31163                     height > this.imageEl.OriginHeight
31164                 )
31165         ){
31166             return false;
31167         }
31168         
31169         return true;
31170         
31171     },
31172     
31173     onRotateLeft : function(e)
31174     {   
31175         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31176             
31177             var minScale = this.thumbEl.getWidth() / this.minWidth;
31178             
31179             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31180             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31181             
31182             this.startScale = this.scale;
31183             
31184             while (this.getScaleLevel() < minScale){
31185             
31186                 this.scale = this.scale + 1;
31187                 
31188                 if(!this.zoomable()){
31189                     break;
31190                 }
31191                 
31192                 if(
31193                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31194                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31195                 ){
31196                     continue;
31197                 }
31198                 
31199                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31200
31201                 this.draw();
31202                 
31203                 return;
31204             }
31205             
31206             this.scale = this.startScale;
31207             
31208             this.onRotateFail();
31209             
31210             return false;
31211         }
31212         
31213         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31214
31215         if(this.isDocument){
31216             this.setThumbBoxSize();
31217             this.setThumbBoxPosition();
31218             this.setCanvasPosition();
31219         }
31220         
31221         this.draw();
31222         
31223         this.fireEvent('rotate', this, 'left');
31224         
31225     },
31226     
31227     onRotateRight : function(e)
31228     {
31229         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31230             
31231             var minScale = this.thumbEl.getWidth() / this.minWidth;
31232         
31233             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31234             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31235             
31236             this.startScale = this.scale;
31237             
31238             while (this.getScaleLevel() < minScale){
31239             
31240                 this.scale = this.scale + 1;
31241                 
31242                 if(!this.zoomable()){
31243                     break;
31244                 }
31245                 
31246                 if(
31247                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31248                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31249                 ){
31250                     continue;
31251                 }
31252                 
31253                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31254
31255                 this.draw();
31256                 
31257                 return;
31258             }
31259             
31260             this.scale = this.startScale;
31261             
31262             this.onRotateFail();
31263             
31264             return false;
31265         }
31266         
31267         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31268
31269         if(this.isDocument){
31270             this.setThumbBoxSize();
31271             this.setThumbBoxPosition();
31272             this.setCanvasPosition();
31273         }
31274         
31275         this.draw();
31276         
31277         this.fireEvent('rotate', this, 'right');
31278     },
31279     
31280     onRotateFail : function()
31281     {
31282         this.errorEl.show(true);
31283         
31284         var _this = this;
31285         
31286         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31287     },
31288     
31289     draw : function()
31290     {
31291         this.previewEl.dom.innerHTML = '';
31292         
31293         var canvasEl = document.createElement("canvas");
31294         
31295         var contextEl = canvasEl.getContext("2d");
31296         
31297         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31298         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31299         var center = this.imageEl.OriginWidth / 2;
31300         
31301         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31302             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31303             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31304             center = this.imageEl.OriginHeight / 2;
31305         }
31306         
31307         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31308         
31309         contextEl.translate(center, center);
31310         contextEl.rotate(this.rotate * Math.PI / 180);
31311
31312         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31313         
31314         this.canvasEl = document.createElement("canvas");
31315         
31316         this.contextEl = this.canvasEl.getContext("2d");
31317         
31318         switch (this.rotate) {
31319             case 0 :
31320                 
31321                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31322                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31323                 
31324                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31325                 
31326                 break;
31327             case 90 : 
31328                 
31329                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31330                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31331                 
31332                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31333                     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);
31334                     break;
31335                 }
31336                 
31337                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31338                 
31339                 break;
31340             case 180 :
31341                 
31342                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31343                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31344                 
31345                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31346                     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);
31347                     break;
31348                 }
31349                 
31350                 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);
31351                 
31352                 break;
31353             case 270 :
31354                 
31355                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31356                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31357         
31358                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31359                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31360                     break;
31361                 }
31362                 
31363                 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);
31364                 
31365                 break;
31366             default : 
31367                 break;
31368         }
31369         
31370         this.previewEl.appendChild(this.canvasEl);
31371         
31372         this.setCanvasPosition();
31373     },
31374     
31375     crop : function()
31376     {
31377         if(!this.canvasLoaded){
31378             return;
31379         }
31380         
31381         var imageCanvas = document.createElement("canvas");
31382         
31383         var imageContext = imageCanvas.getContext("2d");
31384         
31385         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31386         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31387         
31388         var center = imageCanvas.width / 2;
31389         
31390         imageContext.translate(center, center);
31391         
31392         imageContext.rotate(this.rotate * Math.PI / 180);
31393         
31394         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31395         
31396         var canvas = document.createElement("canvas");
31397         
31398         var context = canvas.getContext("2d");
31399                 
31400         canvas.width = this.minWidth;
31401         canvas.height = this.minHeight;
31402
31403         switch (this.rotate) {
31404             case 0 :
31405                 
31406                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31407                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31408                 
31409                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31410                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31411                 
31412                 var targetWidth = this.minWidth - 2 * x;
31413                 var targetHeight = this.minHeight - 2 * y;
31414                 
31415                 var scale = 1;
31416                 
31417                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31418                     scale = targetWidth / width;
31419                 }
31420                 
31421                 if(x > 0 && y == 0){
31422                     scale = targetHeight / height;
31423                 }
31424                 
31425                 if(x > 0 && y > 0){
31426                     scale = targetWidth / width;
31427                     
31428                     if(width < height){
31429                         scale = targetHeight / height;
31430                     }
31431                 }
31432                 
31433                 context.scale(scale, scale);
31434                 
31435                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31436                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31437
31438                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31439                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31440
31441                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31442                 
31443                 break;
31444             case 90 : 
31445                 
31446                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31447                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31448                 
31449                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31450                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31451                 
31452                 var targetWidth = this.minWidth - 2 * x;
31453                 var targetHeight = this.minHeight - 2 * y;
31454                 
31455                 var scale = 1;
31456                 
31457                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31458                     scale = targetWidth / width;
31459                 }
31460                 
31461                 if(x > 0 && y == 0){
31462                     scale = targetHeight / height;
31463                 }
31464                 
31465                 if(x > 0 && y > 0){
31466                     scale = targetWidth / width;
31467                     
31468                     if(width < height){
31469                         scale = targetHeight / height;
31470                     }
31471                 }
31472                 
31473                 context.scale(scale, scale);
31474                 
31475                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31476                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31477
31478                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31479                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31480                 
31481                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31482                 
31483                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31484                 
31485                 break;
31486             case 180 :
31487                 
31488                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31489                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31490                 
31491                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31492                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31493                 
31494                 var targetWidth = this.minWidth - 2 * x;
31495                 var targetHeight = this.minHeight - 2 * y;
31496                 
31497                 var scale = 1;
31498                 
31499                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31500                     scale = targetWidth / width;
31501                 }
31502                 
31503                 if(x > 0 && y == 0){
31504                     scale = targetHeight / height;
31505                 }
31506                 
31507                 if(x > 0 && y > 0){
31508                     scale = targetWidth / width;
31509                     
31510                     if(width < height){
31511                         scale = targetHeight / height;
31512                     }
31513                 }
31514                 
31515                 context.scale(scale, scale);
31516                 
31517                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31518                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31519
31520                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31521                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31522
31523                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31524                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31525                 
31526                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31527                 
31528                 break;
31529             case 270 :
31530                 
31531                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31532                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31533                 
31534                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31535                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31536                 
31537                 var targetWidth = this.minWidth - 2 * x;
31538                 var targetHeight = this.minHeight - 2 * y;
31539                 
31540                 var scale = 1;
31541                 
31542                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31543                     scale = targetWidth / width;
31544                 }
31545                 
31546                 if(x > 0 && y == 0){
31547                     scale = targetHeight / height;
31548                 }
31549                 
31550                 if(x > 0 && y > 0){
31551                     scale = targetWidth / width;
31552                     
31553                     if(width < height){
31554                         scale = targetHeight / height;
31555                     }
31556                 }
31557                 
31558                 context.scale(scale, scale);
31559                 
31560                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31561                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31562
31563                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31564                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31565                 
31566                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31567                 
31568                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31569                 
31570                 break;
31571             default : 
31572                 break;
31573         }
31574         
31575         this.cropData = canvas.toDataURL(this.cropType);
31576         
31577         if(this.fireEvent('crop', this, this.cropData) !== false){
31578             this.process(this.file, this.cropData);
31579         }
31580         
31581         return;
31582         
31583     },
31584     
31585     setThumbBoxSize : function()
31586     {
31587         var width, height;
31588         
31589         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31590             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31591             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31592             
31593             this.minWidth = width;
31594             this.minHeight = height;
31595             
31596             if(this.rotate == 90 || this.rotate == 270){
31597                 this.minWidth = height;
31598                 this.minHeight = width;
31599             }
31600         }
31601         
31602         height = 300;
31603         width = Math.ceil(this.minWidth * height / this.minHeight);
31604         
31605         if(this.minWidth > this.minHeight){
31606             width = 300;
31607             height = Math.ceil(this.minHeight * width / this.minWidth);
31608         }
31609         
31610         this.thumbEl.setStyle({
31611             width : width + 'px',
31612             height : height + 'px'
31613         });
31614
31615         return;
31616             
31617     },
31618     
31619     setThumbBoxPosition : function()
31620     {
31621         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31622         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31623         
31624         this.thumbEl.setLeft(x);
31625         this.thumbEl.setTop(y);
31626         
31627     },
31628     
31629     baseRotateLevel : function()
31630     {
31631         this.baseRotate = 1;
31632         
31633         if(
31634                 typeof(this.exif) != 'undefined' &&
31635                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31636                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31637         ){
31638             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31639         }
31640         
31641         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31642         
31643     },
31644     
31645     baseScaleLevel : function()
31646     {
31647         var width, height;
31648         
31649         if(this.isDocument){
31650             
31651             if(this.baseRotate == 6 || this.baseRotate == 8){
31652             
31653                 height = this.thumbEl.getHeight();
31654                 this.baseScale = height / this.imageEl.OriginWidth;
31655
31656                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31657                     width = this.thumbEl.getWidth();
31658                     this.baseScale = width / this.imageEl.OriginHeight;
31659                 }
31660
31661                 return;
31662             }
31663
31664             height = this.thumbEl.getHeight();
31665             this.baseScale = height / this.imageEl.OriginHeight;
31666
31667             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31668                 width = this.thumbEl.getWidth();
31669                 this.baseScale = width / this.imageEl.OriginWidth;
31670             }
31671
31672             return;
31673         }
31674         
31675         if(this.baseRotate == 6 || this.baseRotate == 8){
31676             
31677             width = this.thumbEl.getHeight();
31678             this.baseScale = width / this.imageEl.OriginHeight;
31679             
31680             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31681                 height = this.thumbEl.getWidth();
31682                 this.baseScale = height / this.imageEl.OriginHeight;
31683             }
31684             
31685             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31686                 height = this.thumbEl.getWidth();
31687                 this.baseScale = height / this.imageEl.OriginHeight;
31688                 
31689                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31690                     width = this.thumbEl.getHeight();
31691                     this.baseScale = width / this.imageEl.OriginWidth;
31692                 }
31693             }
31694             
31695             return;
31696         }
31697         
31698         width = this.thumbEl.getWidth();
31699         this.baseScale = width / this.imageEl.OriginWidth;
31700         
31701         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31702             height = this.thumbEl.getHeight();
31703             this.baseScale = height / this.imageEl.OriginHeight;
31704         }
31705         
31706         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31707             
31708             height = this.thumbEl.getHeight();
31709             this.baseScale = height / this.imageEl.OriginHeight;
31710             
31711             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31712                 width = this.thumbEl.getWidth();
31713                 this.baseScale = width / this.imageEl.OriginWidth;
31714             }
31715             
31716         }
31717         
31718         return;
31719     },
31720     
31721     getScaleLevel : function()
31722     {
31723         return this.baseScale * Math.pow(1.1, this.scale);
31724     },
31725     
31726     onTouchStart : function(e)
31727     {
31728         if(!this.canvasLoaded){
31729             this.beforeSelectFile(e);
31730             return;
31731         }
31732         
31733         var touches = e.browserEvent.touches;
31734         
31735         if(!touches){
31736             return;
31737         }
31738         
31739         if(touches.length == 1){
31740             this.onMouseDown(e);
31741             return;
31742         }
31743         
31744         if(touches.length != 2){
31745             return;
31746         }
31747         
31748         var coords = [];
31749         
31750         for(var i = 0, finger; finger = touches[i]; i++){
31751             coords.push(finger.pageX, finger.pageY);
31752         }
31753         
31754         var x = Math.pow(coords[0] - coords[2], 2);
31755         var y = Math.pow(coords[1] - coords[3], 2);
31756         
31757         this.startDistance = Math.sqrt(x + y);
31758         
31759         this.startScale = this.scale;
31760         
31761         this.pinching = true;
31762         this.dragable = false;
31763         
31764     },
31765     
31766     onTouchMove : function(e)
31767     {
31768         if(!this.pinching && !this.dragable){
31769             return;
31770         }
31771         
31772         var touches = e.browserEvent.touches;
31773         
31774         if(!touches){
31775             return;
31776         }
31777         
31778         if(this.dragable){
31779             this.onMouseMove(e);
31780             return;
31781         }
31782         
31783         var coords = [];
31784         
31785         for(var i = 0, finger; finger = touches[i]; i++){
31786             coords.push(finger.pageX, finger.pageY);
31787         }
31788         
31789         var x = Math.pow(coords[0] - coords[2], 2);
31790         var y = Math.pow(coords[1] - coords[3], 2);
31791         
31792         this.endDistance = Math.sqrt(x + y);
31793         
31794         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31795         
31796         if(!this.zoomable()){
31797             this.scale = this.startScale;
31798             return;
31799         }
31800         
31801         this.draw();
31802         
31803     },
31804     
31805     onTouchEnd : function(e)
31806     {
31807         this.pinching = false;
31808         this.dragable = false;
31809         
31810     },
31811     
31812     process : function(file, crop)
31813     {
31814         if(this.loadMask){
31815             this.maskEl.mask(this.loadingText);
31816         }
31817         
31818         this.xhr = new XMLHttpRequest();
31819         
31820         file.xhr = this.xhr;
31821
31822         this.xhr.open(this.method, this.url, true);
31823         
31824         var headers = {
31825             "Accept": "application/json",
31826             "Cache-Control": "no-cache",
31827             "X-Requested-With": "XMLHttpRequest"
31828         };
31829         
31830         for (var headerName in headers) {
31831             var headerValue = headers[headerName];
31832             if (headerValue) {
31833                 this.xhr.setRequestHeader(headerName, headerValue);
31834             }
31835         }
31836         
31837         var _this = this;
31838         
31839         this.xhr.onload = function()
31840         {
31841             _this.xhrOnLoad(_this.xhr);
31842         }
31843         
31844         this.xhr.onerror = function()
31845         {
31846             _this.xhrOnError(_this.xhr);
31847         }
31848         
31849         var formData = new FormData();
31850
31851         formData.append('returnHTML', 'NO');
31852         
31853         if(crop){
31854             formData.append('crop', crop);
31855         }
31856         
31857         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31858             formData.append(this.paramName, file, file.name);
31859         }
31860         
31861         if(typeof(file.filename) != 'undefined'){
31862             formData.append('filename', file.filename);
31863         }
31864         
31865         if(typeof(file.mimetype) != 'undefined'){
31866             formData.append('mimetype', file.mimetype);
31867         }
31868         
31869         if(this.fireEvent('arrange', this, formData) != false){
31870             this.xhr.send(formData);
31871         };
31872     },
31873     
31874     xhrOnLoad : function(xhr)
31875     {
31876         if(this.loadMask){
31877             this.maskEl.unmask();
31878         }
31879         
31880         if (xhr.readyState !== 4) {
31881             this.fireEvent('exception', this, xhr);
31882             return;
31883         }
31884
31885         var response = Roo.decode(xhr.responseText);
31886         
31887         if(!response.success){
31888             this.fireEvent('exception', this, xhr);
31889             return;
31890         }
31891         
31892         var response = Roo.decode(xhr.responseText);
31893         
31894         this.fireEvent('upload', this, response);
31895         
31896     },
31897     
31898     xhrOnError : function()
31899     {
31900         if(this.loadMask){
31901             this.maskEl.unmask();
31902         }
31903         
31904         Roo.log('xhr on error');
31905         
31906         var response = Roo.decode(xhr.responseText);
31907           
31908         Roo.log(response);
31909         
31910     },
31911     
31912     prepare : function(file)
31913     {   
31914         if(this.loadMask){
31915             this.maskEl.mask(this.loadingText);
31916         }
31917         
31918         this.file = false;
31919         this.exif = {};
31920         
31921         if(typeof(file) === 'string'){
31922             this.loadCanvas(file);
31923             return;
31924         }
31925         
31926         if(!file || !this.urlAPI){
31927             return;
31928         }
31929         
31930         this.file = file;
31931         this.cropType = file.type;
31932         
31933         var _this = this;
31934         
31935         if(this.fireEvent('prepare', this, this.file) != false){
31936             
31937             var reader = new FileReader();
31938             
31939             reader.onload = function (e) {
31940                 if (e.target.error) {
31941                     Roo.log(e.target.error);
31942                     return;
31943                 }
31944                 
31945                 var buffer = e.target.result,
31946                     dataView = new DataView(buffer),
31947                     offset = 2,
31948                     maxOffset = dataView.byteLength - 4,
31949                     markerBytes,
31950                     markerLength;
31951                 
31952                 if (dataView.getUint16(0) === 0xffd8) {
31953                     while (offset < maxOffset) {
31954                         markerBytes = dataView.getUint16(offset);
31955                         
31956                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31957                             markerLength = dataView.getUint16(offset + 2) + 2;
31958                             if (offset + markerLength > dataView.byteLength) {
31959                                 Roo.log('Invalid meta data: Invalid segment size.');
31960                                 break;
31961                             }
31962                             
31963                             if(markerBytes == 0xffe1){
31964                                 _this.parseExifData(
31965                                     dataView,
31966                                     offset,
31967                                     markerLength
31968                                 );
31969                             }
31970                             
31971                             offset += markerLength;
31972                             
31973                             continue;
31974                         }
31975                         
31976                         break;
31977                     }
31978                     
31979                 }
31980                 
31981                 var url = _this.urlAPI.createObjectURL(_this.file);
31982                 
31983                 _this.loadCanvas(url);
31984                 
31985                 return;
31986             }
31987             
31988             reader.readAsArrayBuffer(this.file);
31989             
31990         }
31991         
31992     },
31993     
31994     parseExifData : function(dataView, offset, length)
31995     {
31996         var tiffOffset = offset + 10,
31997             littleEndian,
31998             dirOffset;
31999     
32000         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32001             // No Exif data, might be XMP data instead
32002             return;
32003         }
32004         
32005         // Check for the ASCII code for "Exif" (0x45786966):
32006         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32007             // No Exif data, might be XMP data instead
32008             return;
32009         }
32010         if (tiffOffset + 8 > dataView.byteLength) {
32011             Roo.log('Invalid Exif data: Invalid segment size.');
32012             return;
32013         }
32014         // Check for the two null bytes:
32015         if (dataView.getUint16(offset + 8) !== 0x0000) {
32016             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32017             return;
32018         }
32019         // Check the byte alignment:
32020         switch (dataView.getUint16(tiffOffset)) {
32021         case 0x4949:
32022             littleEndian = true;
32023             break;
32024         case 0x4D4D:
32025             littleEndian = false;
32026             break;
32027         default:
32028             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32029             return;
32030         }
32031         // Check for the TIFF tag marker (0x002A):
32032         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32033             Roo.log('Invalid Exif data: Missing TIFF marker.');
32034             return;
32035         }
32036         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32037         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32038         
32039         this.parseExifTags(
32040             dataView,
32041             tiffOffset,
32042             tiffOffset + dirOffset,
32043             littleEndian
32044         );
32045     },
32046     
32047     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32048     {
32049         var tagsNumber,
32050             dirEndOffset,
32051             i;
32052         if (dirOffset + 6 > dataView.byteLength) {
32053             Roo.log('Invalid Exif data: Invalid directory offset.');
32054             return;
32055         }
32056         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32057         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32058         if (dirEndOffset + 4 > dataView.byteLength) {
32059             Roo.log('Invalid Exif data: Invalid directory size.');
32060             return;
32061         }
32062         for (i = 0; i < tagsNumber; i += 1) {
32063             this.parseExifTag(
32064                 dataView,
32065                 tiffOffset,
32066                 dirOffset + 2 + 12 * i, // tag offset
32067                 littleEndian
32068             );
32069         }
32070         // Return the offset to the next directory:
32071         return dataView.getUint32(dirEndOffset, littleEndian);
32072     },
32073     
32074     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32075     {
32076         var tag = dataView.getUint16(offset, littleEndian);
32077         
32078         this.exif[tag] = this.getExifValue(
32079             dataView,
32080             tiffOffset,
32081             offset,
32082             dataView.getUint16(offset + 2, littleEndian), // tag type
32083             dataView.getUint32(offset + 4, littleEndian), // tag length
32084             littleEndian
32085         );
32086     },
32087     
32088     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32089     {
32090         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32091             tagSize,
32092             dataOffset,
32093             values,
32094             i,
32095             str,
32096             c;
32097     
32098         if (!tagType) {
32099             Roo.log('Invalid Exif data: Invalid tag type.');
32100             return;
32101         }
32102         
32103         tagSize = tagType.size * length;
32104         // Determine if the value is contained in the dataOffset bytes,
32105         // or if the value at the dataOffset is a pointer to the actual data:
32106         dataOffset = tagSize > 4 ?
32107                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32108         if (dataOffset + tagSize > dataView.byteLength) {
32109             Roo.log('Invalid Exif data: Invalid data offset.');
32110             return;
32111         }
32112         if (length === 1) {
32113             return tagType.getValue(dataView, dataOffset, littleEndian);
32114         }
32115         values = [];
32116         for (i = 0; i < length; i += 1) {
32117             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32118         }
32119         
32120         if (tagType.ascii) {
32121             str = '';
32122             // Concatenate the chars:
32123             for (i = 0; i < values.length; i += 1) {
32124                 c = values[i];
32125                 // Ignore the terminating NULL byte(s):
32126                 if (c === '\u0000') {
32127                     break;
32128                 }
32129                 str += c;
32130             }
32131             return str;
32132         }
32133         return values;
32134     }
32135     
32136 });
32137
32138 Roo.apply(Roo.bootstrap.UploadCropbox, {
32139     tags : {
32140         'Orientation': 0x0112
32141     },
32142     
32143     Orientation: {
32144             1: 0, //'top-left',
32145 //            2: 'top-right',
32146             3: 180, //'bottom-right',
32147 //            4: 'bottom-left',
32148 //            5: 'left-top',
32149             6: 90, //'right-top',
32150 //            7: 'right-bottom',
32151             8: 270 //'left-bottom'
32152     },
32153     
32154     exifTagTypes : {
32155         // byte, 8-bit unsigned int:
32156         1: {
32157             getValue: function (dataView, dataOffset) {
32158                 return dataView.getUint8(dataOffset);
32159             },
32160             size: 1
32161         },
32162         // ascii, 8-bit byte:
32163         2: {
32164             getValue: function (dataView, dataOffset) {
32165                 return String.fromCharCode(dataView.getUint8(dataOffset));
32166             },
32167             size: 1,
32168             ascii: true
32169         },
32170         // short, 16 bit int:
32171         3: {
32172             getValue: function (dataView, dataOffset, littleEndian) {
32173                 return dataView.getUint16(dataOffset, littleEndian);
32174             },
32175             size: 2
32176         },
32177         // long, 32 bit int:
32178         4: {
32179             getValue: function (dataView, dataOffset, littleEndian) {
32180                 return dataView.getUint32(dataOffset, littleEndian);
32181             },
32182             size: 4
32183         },
32184         // rational = two long values, first is numerator, second is denominator:
32185         5: {
32186             getValue: function (dataView, dataOffset, littleEndian) {
32187                 return dataView.getUint32(dataOffset, littleEndian) /
32188                     dataView.getUint32(dataOffset + 4, littleEndian);
32189             },
32190             size: 8
32191         },
32192         // slong, 32 bit signed int:
32193         9: {
32194             getValue: function (dataView, dataOffset, littleEndian) {
32195                 return dataView.getInt32(dataOffset, littleEndian);
32196             },
32197             size: 4
32198         },
32199         // srational, two slongs, first is numerator, second is denominator:
32200         10: {
32201             getValue: function (dataView, dataOffset, littleEndian) {
32202                 return dataView.getInt32(dataOffset, littleEndian) /
32203                     dataView.getInt32(dataOffset + 4, littleEndian);
32204             },
32205             size: 8
32206         }
32207     },
32208     
32209     footer : {
32210         STANDARD : [
32211             {
32212                 tag : 'div',
32213                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32214                 action : 'rotate-left',
32215                 cn : [
32216                     {
32217                         tag : 'button',
32218                         cls : 'btn btn-default',
32219                         html : '<i class="fa fa-undo"></i>'
32220                     }
32221                 ]
32222             },
32223             {
32224                 tag : 'div',
32225                 cls : 'btn-group roo-upload-cropbox-picture',
32226                 action : 'picture',
32227                 cn : [
32228                     {
32229                         tag : 'button',
32230                         cls : 'btn btn-default',
32231                         html : '<i class="fa fa-picture-o"></i>'
32232                     }
32233                 ]
32234             },
32235             {
32236                 tag : 'div',
32237                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32238                 action : 'rotate-right',
32239                 cn : [
32240                     {
32241                         tag : 'button',
32242                         cls : 'btn btn-default',
32243                         html : '<i class="fa fa-repeat"></i>'
32244                     }
32245                 ]
32246             }
32247         ],
32248         DOCUMENT : [
32249             {
32250                 tag : 'div',
32251                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32252                 action : 'rotate-left',
32253                 cn : [
32254                     {
32255                         tag : 'button',
32256                         cls : 'btn btn-default',
32257                         html : '<i class="fa fa-undo"></i>'
32258                     }
32259                 ]
32260             },
32261             {
32262                 tag : 'div',
32263                 cls : 'btn-group roo-upload-cropbox-download',
32264                 action : 'download',
32265                 cn : [
32266                     {
32267                         tag : 'button',
32268                         cls : 'btn btn-default',
32269                         html : '<i class="fa fa-download"></i>'
32270                     }
32271                 ]
32272             },
32273             {
32274                 tag : 'div',
32275                 cls : 'btn-group roo-upload-cropbox-crop',
32276                 action : 'crop',
32277                 cn : [
32278                     {
32279                         tag : 'button',
32280                         cls : 'btn btn-default',
32281                         html : '<i class="fa fa-crop"></i>'
32282                     }
32283                 ]
32284             },
32285             {
32286                 tag : 'div',
32287                 cls : 'btn-group roo-upload-cropbox-trash',
32288                 action : 'trash',
32289                 cn : [
32290                     {
32291                         tag : 'button',
32292                         cls : 'btn btn-default',
32293                         html : '<i class="fa fa-trash"></i>'
32294                     }
32295                 ]
32296             },
32297             {
32298                 tag : 'div',
32299                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32300                 action : 'rotate-right',
32301                 cn : [
32302                     {
32303                         tag : 'button',
32304                         cls : 'btn btn-default',
32305                         html : '<i class="fa fa-repeat"></i>'
32306                     }
32307                 ]
32308             }
32309         ],
32310         ROTATOR : [
32311             {
32312                 tag : 'div',
32313                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32314                 action : 'rotate-left',
32315                 cn : [
32316                     {
32317                         tag : 'button',
32318                         cls : 'btn btn-default',
32319                         html : '<i class="fa fa-undo"></i>'
32320                     }
32321                 ]
32322             },
32323             {
32324                 tag : 'div',
32325                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32326                 action : 'rotate-right',
32327                 cn : [
32328                     {
32329                         tag : 'button',
32330                         cls : 'btn btn-default',
32331                         html : '<i class="fa fa-repeat"></i>'
32332                     }
32333                 ]
32334             }
32335         ]
32336     }
32337 });
32338
32339 /*
32340 * Licence: LGPL
32341 */
32342
32343 /**
32344  * @class Roo.bootstrap.DocumentManager
32345  * @extends Roo.bootstrap.Component
32346  * Bootstrap DocumentManager class
32347  * @cfg {String} paramName default 'imageUpload'
32348  * @cfg {String} toolTipName default 'filename'
32349  * @cfg {String} method default POST
32350  * @cfg {String} url action url
32351  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32352  * @cfg {Boolean} multiple multiple upload default true
32353  * @cfg {Number} thumbSize default 300
32354  * @cfg {String} fieldLabel
32355  * @cfg {Number} labelWidth default 4
32356  * @cfg {String} labelAlign (left|top) default left
32357  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32358 * @cfg {Number} labellg set the width of label (1-12)
32359  * @cfg {Number} labelmd set the width of label (1-12)
32360  * @cfg {Number} labelsm set the width of label (1-12)
32361  * @cfg {Number} labelxs set the width of label (1-12)
32362  * 
32363  * @constructor
32364  * Create a new DocumentManager
32365  * @param {Object} config The config object
32366  */
32367
32368 Roo.bootstrap.DocumentManager = function(config){
32369     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32370     
32371     this.files = [];
32372     this.delegates = [];
32373     
32374     this.addEvents({
32375         /**
32376          * @event initial
32377          * Fire when initial the DocumentManager
32378          * @param {Roo.bootstrap.DocumentManager} this
32379          */
32380         "initial" : true,
32381         /**
32382          * @event inspect
32383          * inspect selected file
32384          * @param {Roo.bootstrap.DocumentManager} this
32385          * @param {File} file
32386          */
32387         "inspect" : true,
32388         /**
32389          * @event exception
32390          * Fire when xhr load exception
32391          * @param {Roo.bootstrap.DocumentManager} this
32392          * @param {XMLHttpRequest} xhr
32393          */
32394         "exception" : true,
32395         /**
32396          * @event afterupload
32397          * Fire when xhr load exception
32398          * @param {Roo.bootstrap.DocumentManager} this
32399          * @param {XMLHttpRequest} xhr
32400          */
32401         "afterupload" : true,
32402         /**
32403          * @event prepare
32404          * prepare the form data
32405          * @param {Roo.bootstrap.DocumentManager} this
32406          * @param {Object} formData
32407          */
32408         "prepare" : true,
32409         /**
32410          * @event remove
32411          * Fire when remove the file
32412          * @param {Roo.bootstrap.DocumentManager} this
32413          * @param {Object} file
32414          */
32415         "remove" : true,
32416         /**
32417          * @event refresh
32418          * Fire after refresh the file
32419          * @param {Roo.bootstrap.DocumentManager} this
32420          */
32421         "refresh" : true,
32422         /**
32423          * @event click
32424          * Fire after click the image
32425          * @param {Roo.bootstrap.DocumentManager} this
32426          * @param {Object} file
32427          */
32428         "click" : true,
32429         /**
32430          * @event edit
32431          * Fire when upload a image and editable set to true
32432          * @param {Roo.bootstrap.DocumentManager} this
32433          * @param {Object} file
32434          */
32435         "edit" : true,
32436         /**
32437          * @event beforeselectfile
32438          * Fire before select file
32439          * @param {Roo.bootstrap.DocumentManager} this
32440          */
32441         "beforeselectfile" : true,
32442         /**
32443          * @event process
32444          * Fire before process file
32445          * @param {Roo.bootstrap.DocumentManager} this
32446          * @param {Object} file
32447          */
32448         "process" : true,
32449         /**
32450          * @event previewrendered
32451          * Fire when preview rendered
32452          * @param {Roo.bootstrap.DocumentManager} this
32453          * @param {Object} file
32454          */
32455         "previewrendered" : true,
32456         /**
32457          */
32458         "previewResize" : true
32459         
32460     });
32461 };
32462
32463 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32464     
32465     boxes : 0,
32466     inputName : '',
32467     thumbSize : 300,
32468     multiple : true,
32469     files : false,
32470     method : 'POST',
32471     url : '',
32472     paramName : 'imageUpload',
32473     toolTipName : 'filename',
32474     fieldLabel : '',
32475     labelWidth : 4,
32476     labelAlign : 'left',
32477     editable : true,
32478     delegates : false,
32479     xhr : false, 
32480     
32481     labellg : 0,
32482     labelmd : 0,
32483     labelsm : 0,
32484     labelxs : 0,
32485     
32486     getAutoCreate : function()
32487     {   
32488         var managerWidget = {
32489             tag : 'div',
32490             cls : 'roo-document-manager',
32491             cn : [
32492                 {
32493                     tag : 'input',
32494                     cls : 'roo-document-manager-selector',
32495                     type : 'file'
32496                 },
32497                 {
32498                     tag : 'div',
32499                     cls : 'roo-document-manager-uploader',
32500                     cn : [
32501                         {
32502                             tag : 'div',
32503                             cls : 'roo-document-manager-upload-btn',
32504                             html : '<i class="fa fa-plus"></i>'
32505                         }
32506                     ]
32507                     
32508                 }
32509             ]
32510         };
32511         
32512         var content = [
32513             {
32514                 tag : 'div',
32515                 cls : 'column col-md-12',
32516                 cn : managerWidget
32517             }
32518         ];
32519         
32520         if(this.fieldLabel.length){
32521             
32522             content = [
32523                 {
32524                     tag : 'div',
32525                     cls : 'column col-md-12',
32526                     html : this.fieldLabel
32527                 },
32528                 {
32529                     tag : 'div',
32530                     cls : 'column col-md-12',
32531                     cn : managerWidget
32532                 }
32533             ];
32534
32535             if(this.labelAlign == 'left'){
32536                 content = [
32537                     {
32538                         tag : 'div',
32539                         cls : 'column',
32540                         html : this.fieldLabel
32541                     },
32542                     {
32543                         tag : 'div',
32544                         cls : 'column',
32545                         cn : managerWidget
32546                     }
32547                 ];
32548                 
32549                 if(this.labelWidth > 12){
32550                     content[0].style = "width: " + this.labelWidth + 'px';
32551                 }
32552
32553                 if(this.labelWidth < 13 && this.labelmd == 0){
32554                     this.labelmd = this.labelWidth;
32555                 }
32556
32557                 if(this.labellg > 0){
32558                     content[0].cls += ' col-lg-' + this.labellg;
32559                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32560                 }
32561
32562                 if(this.labelmd > 0){
32563                     content[0].cls += ' col-md-' + this.labelmd;
32564                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32565                 }
32566
32567                 if(this.labelsm > 0){
32568                     content[0].cls += ' col-sm-' + this.labelsm;
32569                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32570                 }
32571
32572                 if(this.labelxs > 0){
32573                     content[0].cls += ' col-xs-' + this.labelxs;
32574                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32575                 }
32576                 
32577             }
32578         }
32579         
32580         var cfg = {
32581             tag : 'div',
32582             cls : 'row clearfix',
32583             cn : content
32584         };
32585         
32586         return cfg;
32587         
32588     },
32589     
32590     initEvents : function()
32591     {
32592         this.managerEl = this.el.select('.roo-document-manager', true).first();
32593         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32594         
32595         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32596         this.selectorEl.hide();
32597         
32598         if(this.multiple){
32599             this.selectorEl.attr('multiple', 'multiple');
32600         }
32601         
32602         this.selectorEl.on('change', this.onFileSelected, this);
32603         
32604         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32605         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32606         
32607         this.uploader.on('click', this.onUploaderClick, this);
32608         
32609         this.renderProgressDialog();
32610         
32611         var _this = this;
32612         
32613         window.addEventListener("resize", function() { _this.refresh(); } );
32614         
32615         this.fireEvent('initial', this);
32616     },
32617     
32618     renderProgressDialog : function()
32619     {
32620         var _this = this;
32621         
32622         this.progressDialog = new Roo.bootstrap.Modal({
32623             cls : 'roo-document-manager-progress-dialog',
32624             allow_close : false,
32625             animate : false,
32626             title : '',
32627             buttons : [
32628                 {
32629                     name  :'cancel',
32630                     weight : 'danger',
32631                     html : 'Cancel'
32632                 }
32633             ], 
32634             listeners : { 
32635                 btnclick : function() {
32636                     _this.uploadCancel();
32637                     this.hide();
32638                 }
32639             }
32640         });
32641          
32642         this.progressDialog.render(Roo.get(document.body));
32643          
32644         this.progress = new Roo.bootstrap.Progress({
32645             cls : 'roo-document-manager-progress',
32646             active : true,
32647             striped : true
32648         });
32649         
32650         this.progress.render(this.progressDialog.getChildContainer());
32651         
32652         this.progressBar = new Roo.bootstrap.ProgressBar({
32653             cls : 'roo-document-manager-progress-bar',
32654             aria_valuenow : 0,
32655             aria_valuemin : 0,
32656             aria_valuemax : 12,
32657             panel : 'success'
32658         });
32659         
32660         this.progressBar.render(this.progress.getChildContainer());
32661     },
32662     
32663     onUploaderClick : function(e)
32664     {
32665         e.preventDefault();
32666      
32667         if(this.fireEvent('beforeselectfile', this) != false){
32668             this.selectorEl.dom.click();
32669         }
32670         
32671     },
32672     
32673     onFileSelected : function(e)
32674     {
32675         e.preventDefault();
32676         
32677         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32678             return;
32679         }
32680         
32681         Roo.each(this.selectorEl.dom.files, function(file){
32682             if(this.fireEvent('inspect', this, file) != false){
32683                 this.files.push(file);
32684             }
32685         }, this);
32686         
32687         this.queue();
32688         
32689     },
32690     
32691     queue : function()
32692     {
32693         this.selectorEl.dom.value = '';
32694         
32695         if(!this.files || !this.files.length){
32696             return;
32697         }
32698         
32699         if(this.boxes > 0 && this.files.length > this.boxes){
32700             this.files = this.files.slice(0, this.boxes);
32701         }
32702         
32703         this.uploader.show();
32704         
32705         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32706             this.uploader.hide();
32707         }
32708         
32709         var _this = this;
32710         
32711         var files = [];
32712         
32713         var docs = [];
32714         
32715         Roo.each(this.files, function(file){
32716             
32717             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32718                 var f = this.renderPreview(file);
32719                 files.push(f);
32720                 return;
32721             }
32722             
32723             if(file.type.indexOf('image') != -1){
32724                 this.delegates.push(
32725                     (function(){
32726                         _this.process(file);
32727                     }).createDelegate(this)
32728                 );
32729         
32730                 return;
32731             }
32732             
32733             docs.push(
32734                 (function(){
32735                     _this.process(file);
32736                 }).createDelegate(this)
32737             );
32738             
32739         }, this);
32740         
32741         this.files = files;
32742         
32743         this.delegates = this.delegates.concat(docs);
32744         
32745         if(!this.delegates.length){
32746             this.refresh();
32747             return;
32748         }
32749         
32750         this.progressBar.aria_valuemax = this.delegates.length;
32751         
32752         this.arrange();
32753         
32754         return;
32755     },
32756     
32757     arrange : function()
32758     {
32759         if(!this.delegates.length){
32760             this.progressDialog.hide();
32761             this.refresh();
32762             return;
32763         }
32764         
32765         var delegate = this.delegates.shift();
32766         
32767         this.progressDialog.show();
32768         
32769         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32770         
32771         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32772         
32773         delegate();
32774     },
32775     
32776     refresh : function()
32777     {
32778         this.uploader.show();
32779         
32780         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32781             this.uploader.hide();
32782         }
32783         
32784         Roo.isTouch ? this.closable(false) : this.closable(true);
32785         
32786         this.fireEvent('refresh', this);
32787     },
32788     
32789     onRemove : function(e, el, o)
32790     {
32791         e.preventDefault();
32792         
32793         this.fireEvent('remove', this, o);
32794         
32795     },
32796     
32797     remove : function(o)
32798     {
32799         var files = [];
32800         
32801         Roo.each(this.files, function(file){
32802             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32803                 files.push(file);
32804                 return;
32805             }
32806
32807             o.target.remove();
32808
32809         }, this);
32810         
32811         this.files = files;
32812         
32813         this.refresh();
32814     },
32815     
32816     clear : function()
32817     {
32818         Roo.each(this.files, function(file){
32819             if(!file.target){
32820                 return;
32821             }
32822             
32823             file.target.remove();
32824
32825         }, this);
32826         
32827         this.files = [];
32828         
32829         this.refresh();
32830     },
32831     
32832     onClick : function(e, el, o)
32833     {
32834         e.preventDefault();
32835         
32836         this.fireEvent('click', this, o);
32837         
32838     },
32839     
32840     closable : function(closable)
32841     {
32842         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32843             
32844             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32845             
32846             if(closable){
32847                 el.show();
32848                 return;
32849             }
32850             
32851             el.hide();
32852             
32853         }, this);
32854     },
32855     
32856     xhrOnLoad : function(xhr)
32857     {
32858         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32859             el.remove();
32860         }, this);
32861         
32862         if (xhr.readyState !== 4) {
32863             this.arrange();
32864             this.fireEvent('exception', this, xhr);
32865             return;
32866         }
32867
32868         var response = Roo.decode(xhr.responseText);
32869         
32870         if(!response.success){
32871             this.arrange();
32872             this.fireEvent('exception', this, xhr);
32873             return;
32874         }
32875         
32876         var file = this.renderPreview(response.data);
32877         
32878         this.files.push(file);
32879         
32880         this.arrange();
32881         
32882         this.fireEvent('afterupload', this, xhr);
32883         
32884     },
32885     
32886     xhrOnError : function(xhr)
32887     {
32888         Roo.log('xhr on error');
32889         
32890         var response = Roo.decode(xhr.responseText);
32891           
32892         Roo.log(response);
32893         
32894         this.arrange();
32895     },
32896     
32897     process : function(file)
32898     {
32899         if(this.fireEvent('process', this, file) !== false){
32900             if(this.editable && file.type.indexOf('image') != -1){
32901                 this.fireEvent('edit', this, file);
32902                 return;
32903             }
32904
32905             this.uploadStart(file, false);
32906
32907             return;
32908         }
32909         
32910     },
32911     
32912     uploadStart : function(file, crop)
32913     {
32914         this.xhr = new XMLHttpRequest();
32915         
32916         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32917             this.arrange();
32918             return;
32919         }
32920         
32921         file.xhr = this.xhr;
32922             
32923         this.managerEl.createChild({
32924             tag : 'div',
32925             cls : 'roo-document-manager-loading',
32926             cn : [
32927                 {
32928                     tag : 'div',
32929                     tooltip : file.name,
32930                     cls : 'roo-document-manager-thumb',
32931                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32932                 }
32933             ]
32934
32935         });
32936
32937         this.xhr.open(this.method, this.url, true);
32938         
32939         var headers = {
32940             "Accept": "application/json",
32941             "Cache-Control": "no-cache",
32942             "X-Requested-With": "XMLHttpRequest"
32943         };
32944         
32945         for (var headerName in headers) {
32946             var headerValue = headers[headerName];
32947             if (headerValue) {
32948                 this.xhr.setRequestHeader(headerName, headerValue);
32949             }
32950         }
32951         
32952         var _this = this;
32953         
32954         this.xhr.onload = function()
32955         {
32956             _this.xhrOnLoad(_this.xhr);
32957         }
32958         
32959         this.xhr.onerror = function()
32960         {
32961             _this.xhrOnError(_this.xhr);
32962         }
32963         
32964         var formData = new FormData();
32965
32966         formData.append('returnHTML', 'NO');
32967         
32968         if(crop){
32969             formData.append('crop', crop);
32970         }
32971         
32972         formData.append(this.paramName, file, file.name);
32973         
32974         var options = {
32975             file : file, 
32976             manually : false
32977         };
32978         
32979         if(this.fireEvent('prepare', this, formData, options) != false){
32980             
32981             if(options.manually){
32982                 return;
32983             }
32984             
32985             this.xhr.send(formData);
32986             return;
32987         };
32988         
32989         this.uploadCancel();
32990     },
32991     
32992     uploadCancel : function()
32993     {
32994         if (this.xhr) {
32995             this.xhr.abort();
32996         }
32997         
32998         this.delegates = [];
32999         
33000         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33001             el.remove();
33002         }, this);
33003         
33004         this.arrange();
33005     },
33006     
33007     renderPreview : function(file)
33008     {
33009         if(typeof(file.target) != 'undefined' && file.target){
33010             return file;
33011         }
33012         
33013         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33014         
33015         var previewEl = this.managerEl.createChild({
33016             tag : 'div',
33017             cls : 'roo-document-manager-preview',
33018             cn : [
33019                 {
33020                     tag : 'div',
33021                     tooltip : file[this.toolTipName],
33022                     cls : 'roo-document-manager-thumb',
33023                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33024                 },
33025                 {
33026                     tag : 'button',
33027                     cls : 'close',
33028                     html : '<i class="fa fa-times-circle"></i>'
33029                 }
33030             ]
33031         });
33032
33033         var close = previewEl.select('button.close', true).first();
33034
33035         close.on('click', this.onRemove, this, file);
33036
33037         file.target = previewEl;
33038
33039         var image = previewEl.select('img', true).first();
33040         
33041         var _this = this;
33042         
33043         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33044         
33045         image.on('click', this.onClick, this, file);
33046         
33047         this.fireEvent('previewrendered', this, file);
33048         
33049         return file;
33050         
33051     },
33052     
33053     onPreviewLoad : function(file, image)
33054     {
33055         if(typeof(file.target) == 'undefined' || !file.target){
33056             return;
33057         }
33058         
33059         var width = image.dom.naturalWidth || image.dom.width;
33060         var height = image.dom.naturalHeight || image.dom.height;
33061         
33062         if(!this.previewResize) {
33063             return;
33064         }
33065         
33066         if(width > height){
33067             file.target.addClass('wide');
33068             return;
33069         }
33070         
33071         file.target.addClass('tall');
33072         return;
33073         
33074     },
33075     
33076     uploadFromSource : function(file, crop)
33077     {
33078         this.xhr = new XMLHttpRequest();
33079         
33080         this.managerEl.createChild({
33081             tag : 'div',
33082             cls : 'roo-document-manager-loading',
33083             cn : [
33084                 {
33085                     tag : 'div',
33086                     tooltip : file.name,
33087                     cls : 'roo-document-manager-thumb',
33088                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33089                 }
33090             ]
33091
33092         });
33093
33094         this.xhr.open(this.method, this.url, true);
33095         
33096         var headers = {
33097             "Accept": "application/json",
33098             "Cache-Control": "no-cache",
33099             "X-Requested-With": "XMLHttpRequest"
33100         };
33101         
33102         for (var headerName in headers) {
33103             var headerValue = headers[headerName];
33104             if (headerValue) {
33105                 this.xhr.setRequestHeader(headerName, headerValue);
33106             }
33107         }
33108         
33109         var _this = this;
33110         
33111         this.xhr.onload = function()
33112         {
33113             _this.xhrOnLoad(_this.xhr);
33114         }
33115         
33116         this.xhr.onerror = function()
33117         {
33118             _this.xhrOnError(_this.xhr);
33119         }
33120         
33121         var formData = new FormData();
33122
33123         formData.append('returnHTML', 'NO');
33124         
33125         formData.append('crop', crop);
33126         
33127         if(typeof(file.filename) != 'undefined'){
33128             formData.append('filename', file.filename);
33129         }
33130         
33131         if(typeof(file.mimetype) != 'undefined'){
33132             formData.append('mimetype', file.mimetype);
33133         }
33134         
33135         Roo.log(formData);
33136         
33137         if(this.fireEvent('prepare', this, formData) != false){
33138             this.xhr.send(formData);
33139         };
33140     }
33141 });
33142
33143 /*
33144 * Licence: LGPL
33145 */
33146
33147 /**
33148  * @class Roo.bootstrap.DocumentViewer
33149  * @extends Roo.bootstrap.Component
33150  * Bootstrap DocumentViewer class
33151  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33152  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33153  * 
33154  * @constructor
33155  * Create a new DocumentViewer
33156  * @param {Object} config The config object
33157  */
33158
33159 Roo.bootstrap.DocumentViewer = function(config){
33160     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33161     
33162     this.addEvents({
33163         /**
33164          * @event initial
33165          * Fire after initEvent
33166          * @param {Roo.bootstrap.DocumentViewer} this
33167          */
33168         "initial" : true,
33169         /**
33170          * @event click
33171          * Fire after click
33172          * @param {Roo.bootstrap.DocumentViewer} this
33173          */
33174         "click" : true,
33175         /**
33176          * @event download
33177          * Fire after download button
33178          * @param {Roo.bootstrap.DocumentViewer} this
33179          */
33180         "download" : true,
33181         /**
33182          * @event trash
33183          * Fire after trash button
33184          * @param {Roo.bootstrap.DocumentViewer} this
33185          */
33186         "trash" : true
33187         
33188     });
33189 };
33190
33191 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33192     
33193     showDownload : true,
33194     
33195     showTrash : true,
33196     
33197     getAutoCreate : function()
33198     {
33199         var cfg = {
33200             tag : 'div',
33201             cls : 'roo-document-viewer',
33202             cn : [
33203                 {
33204                     tag : 'div',
33205                     cls : 'roo-document-viewer-body',
33206                     cn : [
33207                         {
33208                             tag : 'div',
33209                             cls : 'roo-document-viewer-thumb',
33210                             cn : [
33211                                 {
33212                                     tag : 'img',
33213                                     cls : 'roo-document-viewer-image'
33214                                 }
33215                             ]
33216                         }
33217                     ]
33218                 },
33219                 {
33220                     tag : 'div',
33221                     cls : 'roo-document-viewer-footer',
33222                     cn : {
33223                         tag : 'div',
33224                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33225                         cn : [
33226                             {
33227                                 tag : 'div',
33228                                 cls : 'btn-group roo-document-viewer-download',
33229                                 cn : [
33230                                     {
33231                                         tag : 'button',
33232                                         cls : 'btn btn-default',
33233                                         html : '<i class="fa fa-download"></i>'
33234                                     }
33235                                 ]
33236                             },
33237                             {
33238                                 tag : 'div',
33239                                 cls : 'btn-group roo-document-viewer-trash',
33240                                 cn : [
33241                                     {
33242                                         tag : 'button',
33243                                         cls : 'btn btn-default',
33244                                         html : '<i class="fa fa-trash"></i>'
33245                                     }
33246                                 ]
33247                             }
33248                         ]
33249                     }
33250                 }
33251             ]
33252         };
33253         
33254         return cfg;
33255     },
33256     
33257     initEvents : function()
33258     {
33259         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33260         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33261         
33262         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33263         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33264         
33265         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33266         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33267         
33268         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33269         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33270         
33271         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33272         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33273         
33274         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33275         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33276         
33277         this.bodyEl.on('click', this.onClick, this);
33278         this.downloadBtn.on('click', this.onDownload, this);
33279         this.trashBtn.on('click', this.onTrash, this);
33280         
33281         this.downloadBtn.hide();
33282         this.trashBtn.hide();
33283         
33284         if(this.showDownload){
33285             this.downloadBtn.show();
33286         }
33287         
33288         if(this.showTrash){
33289             this.trashBtn.show();
33290         }
33291         
33292         if(!this.showDownload && !this.showTrash) {
33293             this.footerEl.hide();
33294         }
33295         
33296     },
33297     
33298     initial : function()
33299     {
33300         this.fireEvent('initial', this);
33301         
33302     },
33303     
33304     onClick : function(e)
33305     {
33306         e.preventDefault();
33307         
33308         this.fireEvent('click', this);
33309     },
33310     
33311     onDownload : function(e)
33312     {
33313         e.preventDefault();
33314         
33315         this.fireEvent('download', this);
33316     },
33317     
33318     onTrash : function(e)
33319     {
33320         e.preventDefault();
33321         
33322         this.fireEvent('trash', this);
33323     }
33324     
33325 });
33326 /*
33327  * - LGPL
33328  *
33329  * nav progress bar
33330  * 
33331  */
33332
33333 /**
33334  * @class Roo.bootstrap.NavProgressBar
33335  * @extends Roo.bootstrap.Component
33336  * Bootstrap NavProgressBar class
33337  * 
33338  * @constructor
33339  * Create a new nav progress bar
33340  * @param {Object} config The config object
33341  */
33342
33343 Roo.bootstrap.NavProgressBar = function(config){
33344     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33345
33346     this.bullets = this.bullets || [];
33347    
33348 //    Roo.bootstrap.NavProgressBar.register(this);
33349      this.addEvents({
33350         /**
33351              * @event changed
33352              * Fires when the active item changes
33353              * @param {Roo.bootstrap.NavProgressBar} this
33354              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33355              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33356          */
33357         'changed': true
33358      });
33359     
33360 };
33361
33362 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33363     
33364     bullets : [],
33365     barItems : [],
33366     
33367     getAutoCreate : function()
33368     {
33369         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33370         
33371         cfg = {
33372             tag : 'div',
33373             cls : 'roo-navigation-bar-group',
33374             cn : [
33375                 {
33376                     tag : 'div',
33377                     cls : 'roo-navigation-top-bar'
33378                 },
33379                 {
33380                     tag : 'div',
33381                     cls : 'roo-navigation-bullets-bar',
33382                     cn : [
33383                         {
33384                             tag : 'ul',
33385                             cls : 'roo-navigation-bar'
33386                         }
33387                     ]
33388                 },
33389                 
33390                 {
33391                     tag : 'div',
33392                     cls : 'roo-navigation-bottom-bar'
33393                 }
33394             ]
33395             
33396         };
33397         
33398         return cfg;
33399         
33400     },
33401     
33402     initEvents: function() 
33403     {
33404         
33405     },
33406     
33407     onRender : function(ct, position) 
33408     {
33409         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33410         
33411         if(this.bullets.length){
33412             Roo.each(this.bullets, function(b){
33413                this.addItem(b);
33414             }, this);
33415         }
33416         
33417         this.format();
33418         
33419     },
33420     
33421     addItem : function(cfg)
33422     {
33423         var item = new Roo.bootstrap.NavProgressItem(cfg);
33424         
33425         item.parentId = this.id;
33426         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33427         
33428         if(cfg.html){
33429             var top = new Roo.bootstrap.Element({
33430                 tag : 'div',
33431                 cls : 'roo-navigation-bar-text'
33432             });
33433             
33434             var bottom = new Roo.bootstrap.Element({
33435                 tag : 'div',
33436                 cls : 'roo-navigation-bar-text'
33437             });
33438             
33439             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33440             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33441             
33442             var topText = new Roo.bootstrap.Element({
33443                 tag : 'span',
33444                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33445             });
33446             
33447             var bottomText = new Roo.bootstrap.Element({
33448                 tag : 'span',
33449                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33450             });
33451             
33452             topText.onRender(top.el, null);
33453             bottomText.onRender(bottom.el, null);
33454             
33455             item.topEl = top;
33456             item.bottomEl = bottom;
33457         }
33458         
33459         this.barItems.push(item);
33460         
33461         return item;
33462     },
33463     
33464     getActive : function()
33465     {
33466         var active = false;
33467         
33468         Roo.each(this.barItems, function(v){
33469             
33470             if (!v.isActive()) {
33471                 return;
33472             }
33473             
33474             active = v;
33475             return false;
33476             
33477         });
33478         
33479         return active;
33480     },
33481     
33482     setActiveItem : function(item)
33483     {
33484         var prev = false;
33485         
33486         Roo.each(this.barItems, function(v){
33487             if (v.rid == item.rid) {
33488                 return ;
33489             }
33490             
33491             if (v.isActive()) {
33492                 v.setActive(false);
33493                 prev = v;
33494             }
33495         });
33496
33497         item.setActive(true);
33498         
33499         this.fireEvent('changed', this, item, prev);
33500     },
33501     
33502     getBarItem: function(rid)
33503     {
33504         var ret = false;
33505         
33506         Roo.each(this.barItems, function(e) {
33507             if (e.rid != rid) {
33508                 return;
33509             }
33510             
33511             ret =  e;
33512             return false;
33513         });
33514         
33515         return ret;
33516     },
33517     
33518     indexOfItem : function(item)
33519     {
33520         var index = false;
33521         
33522         Roo.each(this.barItems, function(v, i){
33523             
33524             if (v.rid != item.rid) {
33525                 return;
33526             }
33527             
33528             index = i;
33529             return false
33530         });
33531         
33532         return index;
33533     },
33534     
33535     setActiveNext : function()
33536     {
33537         var i = this.indexOfItem(this.getActive());
33538         
33539         if (i > this.barItems.length) {
33540             return;
33541         }
33542         
33543         this.setActiveItem(this.barItems[i+1]);
33544     },
33545     
33546     setActivePrev : function()
33547     {
33548         var i = this.indexOfItem(this.getActive());
33549         
33550         if (i  < 1) {
33551             return;
33552         }
33553         
33554         this.setActiveItem(this.barItems[i-1]);
33555     },
33556     
33557     format : function()
33558     {
33559         if(!this.barItems.length){
33560             return;
33561         }
33562      
33563         var width = 100 / this.barItems.length;
33564         
33565         Roo.each(this.barItems, function(i){
33566             i.el.setStyle('width', width + '%');
33567             i.topEl.el.setStyle('width', width + '%');
33568             i.bottomEl.el.setStyle('width', width + '%');
33569         }, this);
33570         
33571     }
33572     
33573 });
33574 /*
33575  * - LGPL
33576  *
33577  * Nav Progress Item
33578  * 
33579  */
33580
33581 /**
33582  * @class Roo.bootstrap.NavProgressItem
33583  * @extends Roo.bootstrap.Component
33584  * Bootstrap NavProgressItem class
33585  * @cfg {String} rid the reference id
33586  * @cfg {Boolean} active (true|false) Is item active default false
33587  * @cfg {Boolean} disabled (true|false) Is item active default false
33588  * @cfg {String} html
33589  * @cfg {String} position (top|bottom) text position default bottom
33590  * @cfg {String} icon show icon instead of number
33591  * 
33592  * @constructor
33593  * Create a new NavProgressItem
33594  * @param {Object} config The config object
33595  */
33596 Roo.bootstrap.NavProgressItem = function(config){
33597     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33598     this.addEvents({
33599         // raw events
33600         /**
33601          * @event click
33602          * The raw click event for the entire grid.
33603          * @param {Roo.bootstrap.NavProgressItem} this
33604          * @param {Roo.EventObject} e
33605          */
33606         "click" : true
33607     });
33608    
33609 };
33610
33611 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33612     
33613     rid : '',
33614     active : false,
33615     disabled : false,
33616     html : '',
33617     position : 'bottom',
33618     icon : false,
33619     
33620     getAutoCreate : function()
33621     {
33622         var iconCls = 'roo-navigation-bar-item-icon';
33623         
33624         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33625         
33626         var cfg = {
33627             tag: 'li',
33628             cls: 'roo-navigation-bar-item',
33629             cn : [
33630                 {
33631                     tag : 'i',
33632                     cls : iconCls
33633                 }
33634             ]
33635         };
33636         
33637         if(this.active){
33638             cfg.cls += ' active';
33639         }
33640         if(this.disabled){
33641             cfg.cls += ' disabled';
33642         }
33643         
33644         return cfg;
33645     },
33646     
33647     disable : function()
33648     {
33649         this.setDisabled(true);
33650     },
33651     
33652     enable : function()
33653     {
33654         this.setDisabled(false);
33655     },
33656     
33657     initEvents: function() 
33658     {
33659         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33660         
33661         this.iconEl.on('click', this.onClick, this);
33662     },
33663     
33664     onClick : function(e)
33665     {
33666         e.preventDefault();
33667         
33668         if(this.disabled){
33669             return;
33670         }
33671         
33672         if(this.fireEvent('click', this, e) === false){
33673             return;
33674         };
33675         
33676         this.parent().setActiveItem(this);
33677     },
33678     
33679     isActive: function () 
33680     {
33681         return this.active;
33682     },
33683     
33684     setActive : function(state)
33685     {
33686         if(this.active == state){
33687             return;
33688         }
33689         
33690         this.active = state;
33691         
33692         if (state) {
33693             this.el.addClass('active');
33694             return;
33695         }
33696         
33697         this.el.removeClass('active');
33698         
33699         return;
33700     },
33701     
33702     setDisabled : function(state)
33703     {
33704         if(this.disabled == state){
33705             return;
33706         }
33707         
33708         this.disabled = state;
33709         
33710         if (state) {
33711             this.el.addClass('disabled');
33712             return;
33713         }
33714         
33715         this.el.removeClass('disabled');
33716     },
33717     
33718     tooltipEl : function()
33719     {
33720         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33721     }
33722 });
33723  
33724
33725  /*
33726  * - LGPL
33727  *
33728  * FieldLabel
33729  * 
33730  */
33731
33732 /**
33733  * @class Roo.bootstrap.FieldLabel
33734  * @extends Roo.bootstrap.Component
33735  * Bootstrap FieldLabel class
33736  * @cfg {String} html contents of the element
33737  * @cfg {String} tag tag of the element default label
33738  * @cfg {String} cls class of the element
33739  * @cfg {String} target label target 
33740  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33741  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33742  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33743  * @cfg {String} iconTooltip default "This field is required"
33744  * @cfg {String} indicatorpos (left|right) default left
33745  * 
33746  * @constructor
33747  * Create a new FieldLabel
33748  * @param {Object} config The config object
33749  */
33750
33751 Roo.bootstrap.FieldLabel = function(config){
33752     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33753     
33754     this.addEvents({
33755             /**
33756              * @event invalid
33757              * Fires after the field has been marked as invalid.
33758              * @param {Roo.form.FieldLabel} this
33759              * @param {String} msg The validation message
33760              */
33761             invalid : true,
33762             /**
33763              * @event valid
33764              * Fires after the field has been validated with no errors.
33765              * @param {Roo.form.FieldLabel} this
33766              */
33767             valid : true
33768         });
33769 };
33770
33771 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33772     
33773     tag: 'label',
33774     cls: '',
33775     html: '',
33776     target: '',
33777     allowBlank : true,
33778     invalidClass : 'has-warning',
33779     validClass : 'has-success',
33780     iconTooltip : 'This field is required',
33781     indicatorpos : 'left',
33782     
33783     getAutoCreate : function(){
33784         
33785         var cls = "";
33786         if (!this.allowBlank) {
33787             cls  = "visible";
33788         }
33789         
33790         var cfg = {
33791             tag : this.tag,
33792             cls : 'roo-bootstrap-field-label ' + this.cls,
33793             for : this.target,
33794             cn : [
33795                 {
33796                     tag : 'i',
33797                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33798                     tooltip : this.iconTooltip
33799                 },
33800                 {
33801                     tag : 'span',
33802                     html : this.html
33803                 }
33804             ] 
33805         };
33806         
33807         if(this.indicatorpos == 'right'){
33808             var cfg = {
33809                 tag : this.tag,
33810                 cls : 'roo-bootstrap-field-label ' + this.cls,
33811                 for : this.target,
33812                 cn : [
33813                     {
33814                         tag : 'span',
33815                         html : this.html
33816                     },
33817                     {
33818                         tag : 'i',
33819                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33820                         tooltip : this.iconTooltip
33821                     }
33822                 ] 
33823             };
33824         }
33825         
33826         return cfg;
33827     },
33828     
33829     initEvents: function() 
33830     {
33831         Roo.bootstrap.Element.superclass.initEvents.call(this);
33832         
33833         this.indicator = this.indicatorEl();
33834         
33835         if(this.indicator){
33836             this.indicator.removeClass('visible');
33837             this.indicator.addClass('invisible');
33838         }
33839         
33840         Roo.bootstrap.FieldLabel.register(this);
33841     },
33842     
33843     indicatorEl : function()
33844     {
33845         var indicator = this.el.select('i.roo-required-indicator',true).first();
33846         
33847         if(!indicator){
33848             return false;
33849         }
33850         
33851         return indicator;
33852         
33853     },
33854     
33855     /**
33856      * Mark this field as valid
33857      */
33858     markValid : function()
33859     {
33860         if(this.indicator){
33861             this.indicator.removeClass('visible');
33862             this.indicator.addClass('invisible');
33863         }
33864         if (Roo.bootstrap.version == 3) {
33865             this.el.removeClass(this.invalidClass);
33866             this.el.addClass(this.validClass);
33867         } else {
33868             this.el.removeClass('is-invalid');
33869             this.el.addClass('is-valid');
33870         }
33871         
33872         
33873         this.fireEvent('valid', this);
33874     },
33875     
33876     /**
33877      * Mark this field as invalid
33878      * @param {String} msg The validation message
33879      */
33880     markInvalid : function(msg)
33881     {
33882         if(this.indicator){
33883             this.indicator.removeClass('invisible');
33884             this.indicator.addClass('visible');
33885         }
33886           if (Roo.bootstrap.version == 3) {
33887             this.el.removeClass(this.validClass);
33888             this.el.addClass(this.invalidClass);
33889         } else {
33890             this.el.removeClass('is-valid');
33891             this.el.addClass('is-invalid');
33892         }
33893         
33894         
33895         this.fireEvent('invalid', this, msg);
33896     }
33897     
33898    
33899 });
33900
33901 Roo.apply(Roo.bootstrap.FieldLabel, {
33902     
33903     groups: {},
33904     
33905      /**
33906     * register a FieldLabel Group
33907     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33908     */
33909     register : function(label)
33910     {
33911         if(this.groups.hasOwnProperty(label.target)){
33912             return;
33913         }
33914      
33915         this.groups[label.target] = label;
33916         
33917     },
33918     /**
33919     * fetch a FieldLabel Group based on the target
33920     * @param {string} target
33921     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33922     */
33923     get: function(target) {
33924         if (typeof(this.groups[target]) == 'undefined') {
33925             return false;
33926         }
33927         
33928         return this.groups[target] ;
33929     }
33930 });
33931
33932  
33933
33934  /*
33935  * - LGPL
33936  *
33937  * page DateSplitField.
33938  * 
33939  */
33940
33941
33942 /**
33943  * @class Roo.bootstrap.DateSplitField
33944  * @extends Roo.bootstrap.Component
33945  * Bootstrap DateSplitField class
33946  * @cfg {string} fieldLabel - the label associated
33947  * @cfg {Number} labelWidth set the width of label (0-12)
33948  * @cfg {String} labelAlign (top|left)
33949  * @cfg {Boolean} dayAllowBlank (true|false) default false
33950  * @cfg {Boolean} monthAllowBlank (true|false) default false
33951  * @cfg {Boolean} yearAllowBlank (true|false) default false
33952  * @cfg {string} dayPlaceholder 
33953  * @cfg {string} monthPlaceholder
33954  * @cfg {string} yearPlaceholder
33955  * @cfg {string} dayFormat default 'd'
33956  * @cfg {string} monthFormat default 'm'
33957  * @cfg {string} yearFormat default 'Y'
33958  * @cfg {Number} labellg set the width of label (1-12)
33959  * @cfg {Number} labelmd set the width of label (1-12)
33960  * @cfg {Number} labelsm set the width of label (1-12)
33961  * @cfg {Number} labelxs set the width of label (1-12)
33962
33963  *     
33964  * @constructor
33965  * Create a new DateSplitField
33966  * @param {Object} config The config object
33967  */
33968
33969 Roo.bootstrap.DateSplitField = function(config){
33970     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33971     
33972     this.addEvents({
33973         // raw events
33974          /**
33975          * @event years
33976          * getting the data of years
33977          * @param {Roo.bootstrap.DateSplitField} this
33978          * @param {Object} years
33979          */
33980         "years" : true,
33981         /**
33982          * @event days
33983          * getting the data of days
33984          * @param {Roo.bootstrap.DateSplitField} this
33985          * @param {Object} days
33986          */
33987         "days" : true,
33988         /**
33989          * @event invalid
33990          * Fires after the field has been marked as invalid.
33991          * @param {Roo.form.Field} this
33992          * @param {String} msg The validation message
33993          */
33994         invalid : true,
33995        /**
33996          * @event valid
33997          * Fires after the field has been validated with no errors.
33998          * @param {Roo.form.Field} this
33999          */
34000         valid : true
34001     });
34002 };
34003
34004 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
34005     
34006     fieldLabel : '',
34007     labelAlign : 'top',
34008     labelWidth : 3,
34009     dayAllowBlank : false,
34010     monthAllowBlank : false,
34011     yearAllowBlank : false,
34012     dayPlaceholder : '',
34013     monthPlaceholder : '',
34014     yearPlaceholder : '',
34015     dayFormat : 'd',
34016     monthFormat : 'm',
34017     yearFormat : 'Y',
34018     isFormField : true,
34019     labellg : 0,
34020     labelmd : 0,
34021     labelsm : 0,
34022     labelxs : 0,
34023     
34024     getAutoCreate : function()
34025     {
34026         var cfg = {
34027             tag : 'div',
34028             cls : 'row roo-date-split-field-group',
34029             cn : [
34030                 {
34031                     tag : 'input',
34032                     type : 'hidden',
34033                     cls : 'form-hidden-field roo-date-split-field-group-value',
34034                     name : this.name
34035                 }
34036             ]
34037         };
34038         
34039         var labelCls = 'col-md-12';
34040         var contentCls = 'col-md-4';
34041         
34042         if(this.fieldLabel){
34043             
34044             var label = {
34045                 tag : 'div',
34046                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34047                 cn : [
34048                     {
34049                         tag : 'label',
34050                         html : this.fieldLabel
34051                     }
34052                 ]
34053             };
34054             
34055             if(this.labelAlign == 'left'){
34056             
34057                 if(this.labelWidth > 12){
34058                     label.style = "width: " + this.labelWidth + 'px';
34059                 }
34060
34061                 if(this.labelWidth < 13 && this.labelmd == 0){
34062                     this.labelmd = this.labelWidth;
34063                 }
34064
34065                 if(this.labellg > 0){
34066                     labelCls = ' col-lg-' + this.labellg;
34067                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34068                 }
34069
34070                 if(this.labelmd > 0){
34071                     labelCls = ' col-md-' + this.labelmd;
34072                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34073                 }
34074
34075                 if(this.labelsm > 0){
34076                     labelCls = ' col-sm-' + this.labelsm;
34077                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34078                 }
34079
34080                 if(this.labelxs > 0){
34081                     labelCls = ' col-xs-' + this.labelxs;
34082                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34083                 }
34084             }
34085             
34086             label.cls += ' ' + labelCls;
34087             
34088             cfg.cn.push(label);
34089         }
34090         
34091         Roo.each(['day', 'month', 'year'], function(t){
34092             cfg.cn.push({
34093                 tag : 'div',
34094                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34095             });
34096         }, this);
34097         
34098         return cfg;
34099     },
34100     
34101     inputEl: function ()
34102     {
34103         return this.el.select('.roo-date-split-field-group-value', true).first();
34104     },
34105     
34106     onRender : function(ct, position) 
34107     {
34108         var _this = this;
34109         
34110         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34111         
34112         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34113         
34114         this.dayField = new Roo.bootstrap.ComboBox({
34115             allowBlank : this.dayAllowBlank,
34116             alwaysQuery : true,
34117             displayField : 'value',
34118             editable : false,
34119             fieldLabel : '',
34120             forceSelection : true,
34121             mode : 'local',
34122             placeholder : this.dayPlaceholder,
34123             selectOnFocus : true,
34124             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34125             triggerAction : 'all',
34126             typeAhead : true,
34127             valueField : 'value',
34128             store : new Roo.data.SimpleStore({
34129                 data : (function() {    
34130                     var days = [];
34131                     _this.fireEvent('days', _this, days);
34132                     return days;
34133                 })(),
34134                 fields : [ 'value' ]
34135             }),
34136             listeners : {
34137                 select : function (_self, record, index)
34138                 {
34139                     _this.setValue(_this.getValue());
34140                 }
34141             }
34142         });
34143
34144         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34145         
34146         this.monthField = new Roo.bootstrap.MonthField({
34147             after : '<i class=\"fa fa-calendar\"></i>',
34148             allowBlank : this.monthAllowBlank,
34149             placeholder : this.monthPlaceholder,
34150             readOnly : true,
34151             listeners : {
34152                 render : function (_self)
34153                 {
34154                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34155                         e.preventDefault();
34156                         _self.focus();
34157                     });
34158                 },
34159                 select : function (_self, oldvalue, newvalue)
34160                 {
34161                     _this.setValue(_this.getValue());
34162                 }
34163             }
34164         });
34165         
34166         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34167         
34168         this.yearField = new Roo.bootstrap.ComboBox({
34169             allowBlank : this.yearAllowBlank,
34170             alwaysQuery : true,
34171             displayField : 'value',
34172             editable : false,
34173             fieldLabel : '',
34174             forceSelection : true,
34175             mode : 'local',
34176             placeholder : this.yearPlaceholder,
34177             selectOnFocus : true,
34178             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34179             triggerAction : 'all',
34180             typeAhead : true,
34181             valueField : 'value',
34182             store : new Roo.data.SimpleStore({
34183                 data : (function() {
34184                     var years = [];
34185                     _this.fireEvent('years', _this, years);
34186                     return years;
34187                 })(),
34188                 fields : [ 'value' ]
34189             }),
34190             listeners : {
34191                 select : function (_self, record, index)
34192                 {
34193                     _this.setValue(_this.getValue());
34194                 }
34195             }
34196         });
34197
34198         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34199     },
34200     
34201     setValue : function(v, format)
34202     {
34203         this.inputEl.dom.value = v;
34204         
34205         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34206         
34207         var d = Date.parseDate(v, f);
34208         
34209         if(!d){
34210             this.validate();
34211             return;
34212         }
34213         
34214         this.setDay(d.format(this.dayFormat));
34215         this.setMonth(d.format(this.monthFormat));
34216         this.setYear(d.format(this.yearFormat));
34217         
34218         this.validate();
34219         
34220         return;
34221     },
34222     
34223     setDay : function(v)
34224     {
34225         this.dayField.setValue(v);
34226         this.inputEl.dom.value = this.getValue();
34227         this.validate();
34228         return;
34229     },
34230     
34231     setMonth : function(v)
34232     {
34233         this.monthField.setValue(v, true);
34234         this.inputEl.dom.value = this.getValue();
34235         this.validate();
34236         return;
34237     },
34238     
34239     setYear : function(v)
34240     {
34241         this.yearField.setValue(v);
34242         this.inputEl.dom.value = this.getValue();
34243         this.validate();
34244         return;
34245     },
34246     
34247     getDay : function()
34248     {
34249         return this.dayField.getValue();
34250     },
34251     
34252     getMonth : function()
34253     {
34254         return this.monthField.getValue();
34255     },
34256     
34257     getYear : function()
34258     {
34259         return this.yearField.getValue();
34260     },
34261     
34262     getValue : function()
34263     {
34264         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34265         
34266         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34267         
34268         return date;
34269     },
34270     
34271     reset : function()
34272     {
34273         this.setDay('');
34274         this.setMonth('');
34275         this.setYear('');
34276         this.inputEl.dom.value = '';
34277         this.validate();
34278         return;
34279     },
34280     
34281     validate : function()
34282     {
34283         var d = this.dayField.validate();
34284         var m = this.monthField.validate();
34285         var y = this.yearField.validate();
34286         
34287         var valid = true;
34288         
34289         if(
34290                 (!this.dayAllowBlank && !d) ||
34291                 (!this.monthAllowBlank && !m) ||
34292                 (!this.yearAllowBlank && !y)
34293         ){
34294             valid = false;
34295         }
34296         
34297         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34298             return valid;
34299         }
34300         
34301         if(valid){
34302             this.markValid();
34303             return valid;
34304         }
34305         
34306         this.markInvalid();
34307         
34308         return valid;
34309     },
34310     
34311     markValid : function()
34312     {
34313         
34314         var label = this.el.select('label', true).first();
34315         var icon = this.el.select('i.fa-star', true).first();
34316
34317         if(label && icon){
34318             icon.remove();
34319         }
34320         
34321         this.fireEvent('valid', this);
34322     },
34323     
34324      /**
34325      * Mark this field as invalid
34326      * @param {String} msg The validation message
34327      */
34328     markInvalid : function(msg)
34329     {
34330         
34331         var label = this.el.select('label', true).first();
34332         var icon = this.el.select('i.fa-star', true).first();
34333
34334         if(label && !icon){
34335             this.el.select('.roo-date-split-field-label', true).createChild({
34336                 tag : 'i',
34337                 cls : 'text-danger fa fa-lg fa-star',
34338                 tooltip : 'This field is required',
34339                 style : 'margin-right:5px;'
34340             }, label, true);
34341         }
34342         
34343         this.fireEvent('invalid', this, msg);
34344     },
34345     
34346     clearInvalid : function()
34347     {
34348         var label = this.el.select('label', true).first();
34349         var icon = this.el.select('i.fa-star', true).first();
34350
34351         if(label && icon){
34352             icon.remove();
34353         }
34354         
34355         this.fireEvent('valid', this);
34356     },
34357     
34358     getName: function()
34359     {
34360         return this.name;
34361     }
34362     
34363 });
34364
34365  /**
34366  *
34367  * This is based on 
34368  * http://masonry.desandro.com
34369  *
34370  * The idea is to render all the bricks based on vertical width...
34371  *
34372  * The original code extends 'outlayer' - we might need to use that....
34373  * 
34374  */
34375
34376
34377 /**
34378  * @class Roo.bootstrap.LayoutMasonry
34379  * @extends Roo.bootstrap.Component
34380  * Bootstrap Layout Masonry class
34381  * 
34382  * @constructor
34383  * Create a new Element
34384  * @param {Object} config The config object
34385  */
34386
34387 Roo.bootstrap.LayoutMasonry = function(config){
34388     
34389     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34390     
34391     this.bricks = [];
34392     
34393     Roo.bootstrap.LayoutMasonry.register(this);
34394     
34395     this.addEvents({
34396         // raw events
34397         /**
34398          * @event layout
34399          * Fire after layout the items
34400          * @param {Roo.bootstrap.LayoutMasonry} this
34401          * @param {Roo.EventObject} e
34402          */
34403         "layout" : true
34404     });
34405     
34406 };
34407
34408 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34409     
34410     /**
34411      * @cfg {Boolean} isLayoutInstant = no animation?
34412      */   
34413     isLayoutInstant : false, // needed?
34414    
34415     /**
34416      * @cfg {Number} boxWidth  width of the columns
34417      */   
34418     boxWidth : 450,
34419     
34420       /**
34421      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34422      */   
34423     boxHeight : 0,
34424     
34425     /**
34426      * @cfg {Number} padWidth padding below box..
34427      */   
34428     padWidth : 10, 
34429     
34430     /**
34431      * @cfg {Number} gutter gutter width..
34432      */   
34433     gutter : 10,
34434     
34435      /**
34436      * @cfg {Number} maxCols maximum number of columns
34437      */   
34438     
34439     maxCols: 0,
34440     
34441     /**
34442      * @cfg {Boolean} isAutoInitial defalut true
34443      */   
34444     isAutoInitial : true, 
34445     
34446     containerWidth: 0,
34447     
34448     /**
34449      * @cfg {Boolean} isHorizontal defalut false
34450      */   
34451     isHorizontal : false, 
34452
34453     currentSize : null,
34454     
34455     tag: 'div',
34456     
34457     cls: '',
34458     
34459     bricks: null, //CompositeElement
34460     
34461     cols : 1,
34462     
34463     _isLayoutInited : false,
34464     
34465 //    isAlternative : false, // only use for vertical layout...
34466     
34467     /**
34468      * @cfg {Number} alternativePadWidth padding below box..
34469      */   
34470     alternativePadWidth : 50,
34471     
34472     selectedBrick : [],
34473     
34474     getAutoCreate : function(){
34475         
34476         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34477         
34478         var cfg = {
34479             tag: this.tag,
34480             cls: 'blog-masonary-wrapper ' + this.cls,
34481             cn : {
34482                 cls : 'mas-boxes masonary'
34483             }
34484         };
34485         
34486         return cfg;
34487     },
34488     
34489     getChildContainer: function( )
34490     {
34491         if (this.boxesEl) {
34492             return this.boxesEl;
34493         }
34494         
34495         this.boxesEl = this.el.select('.mas-boxes').first();
34496         
34497         return this.boxesEl;
34498     },
34499     
34500     
34501     initEvents : function()
34502     {
34503         var _this = this;
34504         
34505         if(this.isAutoInitial){
34506             Roo.log('hook children rendered');
34507             this.on('childrenrendered', function() {
34508                 Roo.log('children rendered');
34509                 _this.initial();
34510             } ,this);
34511         }
34512     },
34513     
34514     initial : function()
34515     {
34516         this.selectedBrick = [];
34517         
34518         this.currentSize = this.el.getBox(true);
34519         
34520         Roo.EventManager.onWindowResize(this.resize, this); 
34521
34522         if(!this.isAutoInitial){
34523             this.layout();
34524             return;
34525         }
34526         
34527         this.layout();
34528         
34529         return;
34530         //this.layout.defer(500,this);
34531         
34532     },
34533     
34534     resize : function()
34535     {
34536         var cs = this.el.getBox(true);
34537         
34538         if (
34539                 this.currentSize.width == cs.width && 
34540                 this.currentSize.x == cs.x && 
34541                 this.currentSize.height == cs.height && 
34542                 this.currentSize.y == cs.y 
34543         ) {
34544             Roo.log("no change in with or X or Y");
34545             return;
34546         }
34547         
34548         this.currentSize = cs;
34549         
34550         this.layout();
34551         
34552     },
34553     
34554     layout : function()
34555     {   
34556         this._resetLayout();
34557         
34558         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34559         
34560         this.layoutItems( isInstant );
34561       
34562         this._isLayoutInited = true;
34563         
34564         this.fireEvent('layout', this);
34565         
34566     },
34567     
34568     _resetLayout : function()
34569     {
34570         if(this.isHorizontal){
34571             this.horizontalMeasureColumns();
34572             return;
34573         }
34574         
34575         this.verticalMeasureColumns();
34576         
34577     },
34578     
34579     verticalMeasureColumns : function()
34580     {
34581         this.getContainerWidth();
34582         
34583 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34584 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34585 //            return;
34586 //        }
34587         
34588         var boxWidth = this.boxWidth + this.padWidth;
34589         
34590         if(this.containerWidth < this.boxWidth){
34591             boxWidth = this.containerWidth
34592         }
34593         
34594         var containerWidth = this.containerWidth;
34595         
34596         var cols = Math.floor(containerWidth / boxWidth);
34597         
34598         this.cols = Math.max( cols, 1 );
34599         
34600         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34601         
34602         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34603         
34604         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34605         
34606         this.colWidth = boxWidth + avail - this.padWidth;
34607         
34608         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34609         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34610     },
34611     
34612     horizontalMeasureColumns : function()
34613     {
34614         this.getContainerWidth();
34615         
34616         var boxWidth = this.boxWidth;
34617         
34618         if(this.containerWidth < boxWidth){
34619             boxWidth = this.containerWidth;
34620         }
34621         
34622         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34623         
34624         this.el.setHeight(boxWidth);
34625         
34626     },
34627     
34628     getContainerWidth : function()
34629     {
34630         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34631     },
34632     
34633     layoutItems : function( isInstant )
34634     {
34635         Roo.log(this.bricks);
34636         
34637         var items = Roo.apply([], this.bricks);
34638         
34639         if(this.isHorizontal){
34640             this._horizontalLayoutItems( items , isInstant );
34641             return;
34642         }
34643         
34644 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34645 //            this._verticalAlternativeLayoutItems( items , isInstant );
34646 //            return;
34647 //        }
34648         
34649         this._verticalLayoutItems( items , isInstant );
34650         
34651     },
34652     
34653     _verticalLayoutItems : function ( items , isInstant)
34654     {
34655         if ( !items || !items.length ) {
34656             return;
34657         }
34658         
34659         var standard = [
34660             ['xs', 'xs', 'xs', 'tall'],
34661             ['xs', 'xs', 'tall'],
34662             ['xs', 'xs', 'sm'],
34663             ['xs', 'xs', 'xs'],
34664             ['xs', 'tall'],
34665             ['xs', 'sm'],
34666             ['xs', 'xs'],
34667             ['xs'],
34668             
34669             ['sm', 'xs', 'xs'],
34670             ['sm', 'xs'],
34671             ['sm'],
34672             
34673             ['tall', 'xs', 'xs', 'xs'],
34674             ['tall', 'xs', 'xs'],
34675             ['tall', 'xs'],
34676             ['tall']
34677             
34678         ];
34679         
34680         var queue = [];
34681         
34682         var boxes = [];
34683         
34684         var box = [];
34685         
34686         Roo.each(items, function(item, k){
34687             
34688             switch (item.size) {
34689                 // these layouts take up a full box,
34690                 case 'md' :
34691                 case 'md-left' :
34692                 case 'md-right' :
34693                 case 'wide' :
34694                     
34695                     if(box.length){
34696                         boxes.push(box);
34697                         box = [];
34698                     }
34699                     
34700                     boxes.push([item]);
34701                     
34702                     break;
34703                     
34704                 case 'xs' :
34705                 case 'sm' :
34706                 case 'tall' :
34707                     
34708                     box.push(item);
34709                     
34710                     break;
34711                 default :
34712                     break;
34713                     
34714             }
34715             
34716         }, this);
34717         
34718         if(box.length){
34719             boxes.push(box);
34720             box = [];
34721         }
34722         
34723         var filterPattern = function(box, length)
34724         {
34725             if(!box.length){
34726                 return;
34727             }
34728             
34729             var match = false;
34730             
34731             var pattern = box.slice(0, length);
34732             
34733             var format = [];
34734             
34735             Roo.each(pattern, function(i){
34736                 format.push(i.size);
34737             }, this);
34738             
34739             Roo.each(standard, function(s){
34740                 
34741                 if(String(s) != String(format)){
34742                     return;
34743                 }
34744                 
34745                 match = true;
34746                 return false;
34747                 
34748             }, this);
34749             
34750             if(!match && length == 1){
34751                 return;
34752             }
34753             
34754             if(!match){
34755                 filterPattern(box, length - 1);
34756                 return;
34757             }
34758                 
34759             queue.push(pattern);
34760
34761             box = box.slice(length, box.length);
34762
34763             filterPattern(box, 4);
34764
34765             return;
34766             
34767         }
34768         
34769         Roo.each(boxes, function(box, k){
34770             
34771             if(!box.length){
34772                 return;
34773             }
34774             
34775             if(box.length == 1){
34776                 queue.push(box);
34777                 return;
34778             }
34779             
34780             filterPattern(box, 4);
34781             
34782         }, this);
34783         
34784         this._processVerticalLayoutQueue( queue, isInstant );
34785         
34786     },
34787     
34788 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34789 //    {
34790 //        if ( !items || !items.length ) {
34791 //            return;
34792 //        }
34793 //
34794 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34795 //        
34796 //    },
34797     
34798     _horizontalLayoutItems : function ( items , isInstant)
34799     {
34800         if ( !items || !items.length || items.length < 3) {
34801             return;
34802         }
34803         
34804         items.reverse();
34805         
34806         var eItems = items.slice(0, 3);
34807         
34808         items = items.slice(3, items.length);
34809         
34810         var standard = [
34811             ['xs', 'xs', 'xs', 'wide'],
34812             ['xs', 'xs', 'wide'],
34813             ['xs', 'xs', 'sm'],
34814             ['xs', 'xs', 'xs'],
34815             ['xs', 'wide'],
34816             ['xs', 'sm'],
34817             ['xs', 'xs'],
34818             ['xs'],
34819             
34820             ['sm', 'xs', 'xs'],
34821             ['sm', 'xs'],
34822             ['sm'],
34823             
34824             ['wide', 'xs', 'xs', 'xs'],
34825             ['wide', 'xs', 'xs'],
34826             ['wide', 'xs'],
34827             ['wide'],
34828             
34829             ['wide-thin']
34830         ];
34831         
34832         var queue = [];
34833         
34834         var boxes = [];
34835         
34836         var box = [];
34837         
34838         Roo.each(items, function(item, k){
34839             
34840             switch (item.size) {
34841                 case 'md' :
34842                 case 'md-left' :
34843                 case 'md-right' :
34844                 case 'tall' :
34845                     
34846                     if(box.length){
34847                         boxes.push(box);
34848                         box = [];
34849                     }
34850                     
34851                     boxes.push([item]);
34852                     
34853                     break;
34854                     
34855                 case 'xs' :
34856                 case 'sm' :
34857                 case 'wide' :
34858                 case 'wide-thin' :
34859                     
34860                     box.push(item);
34861                     
34862                     break;
34863                 default :
34864                     break;
34865                     
34866             }
34867             
34868         }, this);
34869         
34870         if(box.length){
34871             boxes.push(box);
34872             box = [];
34873         }
34874         
34875         var filterPattern = function(box, length)
34876         {
34877             if(!box.length){
34878                 return;
34879             }
34880             
34881             var match = false;
34882             
34883             var pattern = box.slice(0, length);
34884             
34885             var format = [];
34886             
34887             Roo.each(pattern, function(i){
34888                 format.push(i.size);
34889             }, this);
34890             
34891             Roo.each(standard, function(s){
34892                 
34893                 if(String(s) != String(format)){
34894                     return;
34895                 }
34896                 
34897                 match = true;
34898                 return false;
34899                 
34900             }, this);
34901             
34902             if(!match && length == 1){
34903                 return;
34904             }
34905             
34906             if(!match){
34907                 filterPattern(box, length - 1);
34908                 return;
34909             }
34910                 
34911             queue.push(pattern);
34912
34913             box = box.slice(length, box.length);
34914
34915             filterPattern(box, 4);
34916
34917             return;
34918             
34919         }
34920         
34921         Roo.each(boxes, function(box, k){
34922             
34923             if(!box.length){
34924                 return;
34925             }
34926             
34927             if(box.length == 1){
34928                 queue.push(box);
34929                 return;
34930             }
34931             
34932             filterPattern(box, 4);
34933             
34934         }, this);
34935         
34936         
34937         var prune = [];
34938         
34939         var pos = this.el.getBox(true);
34940         
34941         var minX = pos.x;
34942         
34943         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34944         
34945         var hit_end = false;
34946         
34947         Roo.each(queue, function(box){
34948             
34949             if(hit_end){
34950                 
34951                 Roo.each(box, function(b){
34952                 
34953                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34954                     b.el.hide();
34955
34956                 }, this);
34957
34958                 return;
34959             }
34960             
34961             var mx = 0;
34962             
34963             Roo.each(box, function(b){
34964                 
34965                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34966                 b.el.show();
34967
34968                 mx = Math.max(mx, b.x);
34969                 
34970             }, this);
34971             
34972             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34973             
34974             if(maxX < minX){
34975                 
34976                 Roo.each(box, function(b){
34977                 
34978                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34979                     b.el.hide();
34980                     
34981                 }, this);
34982                 
34983                 hit_end = true;
34984                 
34985                 return;
34986             }
34987             
34988             prune.push(box);
34989             
34990         }, this);
34991         
34992         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34993     },
34994     
34995     /** Sets position of item in DOM
34996     * @param {Element} item
34997     * @param {Number} x - horizontal position
34998     * @param {Number} y - vertical position
34999     * @param {Boolean} isInstant - disables transitions
35000     */
35001     _processVerticalLayoutQueue : function( queue, isInstant )
35002     {
35003         var pos = this.el.getBox(true);
35004         var x = pos.x;
35005         var y = pos.y;
35006         var maxY = [];
35007         
35008         for (var i = 0; i < this.cols; i++){
35009             maxY[i] = pos.y;
35010         }
35011         
35012         Roo.each(queue, function(box, k){
35013             
35014             var col = k % this.cols;
35015             
35016             Roo.each(box, function(b,kk){
35017                 
35018                 b.el.position('absolute');
35019                 
35020                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35021                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35022                 
35023                 if(b.size == 'md-left' || b.size == 'md-right'){
35024                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35025                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35026                 }
35027                 
35028                 b.el.setWidth(width);
35029                 b.el.setHeight(height);
35030                 // iframe?
35031                 b.el.select('iframe',true).setSize(width,height);
35032                 
35033             }, this);
35034             
35035             for (var i = 0; i < this.cols; i++){
35036                 
35037                 if(maxY[i] < maxY[col]){
35038                     col = i;
35039                     continue;
35040                 }
35041                 
35042                 col = Math.min(col, i);
35043                 
35044             }
35045             
35046             x = pos.x + col * (this.colWidth + this.padWidth);
35047             
35048             y = maxY[col];
35049             
35050             var positions = [];
35051             
35052             switch (box.length){
35053                 case 1 :
35054                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35055                     break;
35056                 case 2 :
35057                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35058                     break;
35059                 case 3 :
35060                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35061                     break;
35062                 case 4 :
35063                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35064                     break;
35065                 default :
35066                     break;
35067             }
35068             
35069             Roo.each(box, function(b,kk){
35070                 
35071                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35072                 
35073                 var sz = b.el.getSize();
35074                 
35075                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35076                 
35077             }, this);
35078             
35079         }, this);
35080         
35081         var mY = 0;
35082         
35083         for (var i = 0; i < this.cols; i++){
35084             mY = Math.max(mY, maxY[i]);
35085         }
35086         
35087         this.el.setHeight(mY - pos.y);
35088         
35089     },
35090     
35091 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35092 //    {
35093 //        var pos = this.el.getBox(true);
35094 //        var x = pos.x;
35095 //        var y = pos.y;
35096 //        var maxX = pos.right;
35097 //        
35098 //        var maxHeight = 0;
35099 //        
35100 //        Roo.each(items, function(item, k){
35101 //            
35102 //            var c = k % 2;
35103 //            
35104 //            item.el.position('absolute');
35105 //                
35106 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35107 //
35108 //            item.el.setWidth(width);
35109 //
35110 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35111 //
35112 //            item.el.setHeight(height);
35113 //            
35114 //            if(c == 0){
35115 //                item.el.setXY([x, y], isInstant ? false : true);
35116 //            } else {
35117 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35118 //            }
35119 //            
35120 //            y = y + height + this.alternativePadWidth;
35121 //            
35122 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35123 //            
35124 //        }, this);
35125 //        
35126 //        this.el.setHeight(maxHeight);
35127 //        
35128 //    },
35129     
35130     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35131     {
35132         var pos = this.el.getBox(true);
35133         
35134         var minX = pos.x;
35135         var minY = pos.y;
35136         
35137         var maxX = pos.right;
35138         
35139         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35140         
35141         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35142         
35143         Roo.each(queue, function(box, k){
35144             
35145             Roo.each(box, function(b, kk){
35146                 
35147                 b.el.position('absolute');
35148                 
35149                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35150                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35151                 
35152                 if(b.size == 'md-left' || b.size == 'md-right'){
35153                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35154                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35155                 }
35156                 
35157                 b.el.setWidth(width);
35158                 b.el.setHeight(height);
35159                 
35160             }, this);
35161             
35162             if(!box.length){
35163                 return;
35164             }
35165             
35166             var positions = [];
35167             
35168             switch (box.length){
35169                 case 1 :
35170                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35171                     break;
35172                 case 2 :
35173                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35174                     break;
35175                 case 3 :
35176                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35177                     break;
35178                 case 4 :
35179                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35180                     break;
35181                 default :
35182                     break;
35183             }
35184             
35185             Roo.each(box, function(b,kk){
35186                 
35187                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35188                 
35189                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35190                 
35191             }, this);
35192             
35193         }, this);
35194         
35195     },
35196     
35197     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35198     {
35199         Roo.each(eItems, function(b,k){
35200             
35201             b.size = (k == 0) ? 'sm' : 'xs';
35202             b.x = (k == 0) ? 2 : 1;
35203             b.y = (k == 0) ? 2 : 1;
35204             
35205             b.el.position('absolute');
35206             
35207             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35208                 
35209             b.el.setWidth(width);
35210             
35211             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35212             
35213             b.el.setHeight(height);
35214             
35215         }, this);
35216
35217         var positions = [];
35218         
35219         positions.push({
35220             x : maxX - this.unitWidth * 2 - this.gutter,
35221             y : minY
35222         });
35223         
35224         positions.push({
35225             x : maxX - this.unitWidth,
35226             y : minY + (this.unitWidth + this.gutter) * 2
35227         });
35228         
35229         positions.push({
35230             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35231             y : minY
35232         });
35233         
35234         Roo.each(eItems, function(b,k){
35235             
35236             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35237
35238         }, this);
35239         
35240     },
35241     
35242     getVerticalOneBoxColPositions : function(x, y, box)
35243     {
35244         var pos = [];
35245         
35246         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35247         
35248         if(box[0].size == 'md-left'){
35249             rand = 0;
35250         }
35251         
35252         if(box[0].size == 'md-right'){
35253             rand = 1;
35254         }
35255         
35256         pos.push({
35257             x : x + (this.unitWidth + this.gutter) * rand,
35258             y : y
35259         });
35260         
35261         return pos;
35262     },
35263     
35264     getVerticalTwoBoxColPositions : function(x, y, box)
35265     {
35266         var pos = [];
35267         
35268         if(box[0].size == 'xs'){
35269             
35270             pos.push({
35271                 x : x,
35272                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35273             });
35274
35275             pos.push({
35276                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35277                 y : y
35278             });
35279             
35280             return pos;
35281             
35282         }
35283         
35284         pos.push({
35285             x : x,
35286             y : y
35287         });
35288
35289         pos.push({
35290             x : x + (this.unitWidth + this.gutter) * 2,
35291             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35292         });
35293         
35294         return pos;
35295         
35296     },
35297     
35298     getVerticalThreeBoxColPositions : function(x, y, box)
35299     {
35300         var pos = [];
35301         
35302         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35303             
35304             pos.push({
35305                 x : x,
35306                 y : y
35307             });
35308
35309             pos.push({
35310                 x : x + (this.unitWidth + this.gutter) * 1,
35311                 y : y
35312             });
35313             
35314             pos.push({
35315                 x : x + (this.unitWidth + this.gutter) * 2,
35316                 y : y
35317             });
35318             
35319             return pos;
35320             
35321         }
35322         
35323         if(box[0].size == 'xs' && box[1].size == 'xs'){
35324             
35325             pos.push({
35326                 x : x,
35327                 y : y
35328             });
35329
35330             pos.push({
35331                 x : x,
35332                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35333             });
35334             
35335             pos.push({
35336                 x : x + (this.unitWidth + this.gutter) * 1,
35337                 y : y
35338             });
35339             
35340             return pos;
35341             
35342         }
35343         
35344         pos.push({
35345             x : x,
35346             y : y
35347         });
35348
35349         pos.push({
35350             x : x + (this.unitWidth + this.gutter) * 2,
35351             y : y
35352         });
35353
35354         pos.push({
35355             x : x + (this.unitWidth + this.gutter) * 2,
35356             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35357         });
35358             
35359         return pos;
35360         
35361     },
35362     
35363     getVerticalFourBoxColPositions : function(x, y, box)
35364     {
35365         var pos = [];
35366         
35367         if(box[0].size == 'xs'){
35368             
35369             pos.push({
35370                 x : x,
35371                 y : y
35372             });
35373
35374             pos.push({
35375                 x : x,
35376                 y : y + (this.unitHeight + this.gutter) * 1
35377             });
35378             
35379             pos.push({
35380                 x : x,
35381                 y : y + (this.unitHeight + this.gutter) * 2
35382             });
35383             
35384             pos.push({
35385                 x : x + (this.unitWidth + this.gutter) * 1,
35386                 y : y
35387             });
35388             
35389             return pos;
35390             
35391         }
35392         
35393         pos.push({
35394             x : x,
35395             y : y
35396         });
35397
35398         pos.push({
35399             x : x + (this.unitWidth + this.gutter) * 2,
35400             y : y
35401         });
35402
35403         pos.push({
35404             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35405             y : y + (this.unitHeight + this.gutter) * 1
35406         });
35407
35408         pos.push({
35409             x : x + (this.unitWidth + this.gutter) * 2,
35410             y : y + (this.unitWidth + this.gutter) * 2
35411         });
35412
35413         return pos;
35414         
35415     },
35416     
35417     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35418     {
35419         var pos = [];
35420         
35421         if(box[0].size == 'md-left'){
35422             pos.push({
35423                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35424                 y : minY
35425             });
35426             
35427             return pos;
35428         }
35429         
35430         if(box[0].size == 'md-right'){
35431             pos.push({
35432                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35433                 y : minY + (this.unitWidth + this.gutter) * 1
35434             });
35435             
35436             return pos;
35437         }
35438         
35439         var rand = Math.floor(Math.random() * (4 - box[0].y));
35440         
35441         pos.push({
35442             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35443             y : minY + (this.unitWidth + this.gutter) * rand
35444         });
35445         
35446         return pos;
35447         
35448     },
35449     
35450     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35451     {
35452         var pos = [];
35453         
35454         if(box[0].size == 'xs'){
35455             
35456             pos.push({
35457                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35458                 y : minY
35459             });
35460
35461             pos.push({
35462                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35463                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35464             });
35465             
35466             return pos;
35467             
35468         }
35469         
35470         pos.push({
35471             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35472             y : minY
35473         });
35474
35475         pos.push({
35476             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35477             y : minY + (this.unitWidth + this.gutter) * 2
35478         });
35479         
35480         return pos;
35481         
35482     },
35483     
35484     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35485     {
35486         var pos = [];
35487         
35488         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35489             
35490             pos.push({
35491                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35492                 y : minY
35493             });
35494
35495             pos.push({
35496                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35497                 y : minY + (this.unitWidth + this.gutter) * 1
35498             });
35499             
35500             pos.push({
35501                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35502                 y : minY + (this.unitWidth + this.gutter) * 2
35503             });
35504             
35505             return pos;
35506             
35507         }
35508         
35509         if(box[0].size == 'xs' && box[1].size == 'xs'){
35510             
35511             pos.push({
35512                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35513                 y : minY
35514             });
35515
35516             pos.push({
35517                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35518                 y : minY
35519             });
35520             
35521             pos.push({
35522                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35523                 y : minY + (this.unitWidth + this.gutter) * 1
35524             });
35525             
35526             return pos;
35527             
35528         }
35529         
35530         pos.push({
35531             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35532             y : minY
35533         });
35534
35535         pos.push({
35536             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35537             y : minY + (this.unitWidth + this.gutter) * 2
35538         });
35539
35540         pos.push({
35541             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35542             y : minY + (this.unitWidth + this.gutter) * 2
35543         });
35544             
35545         return pos;
35546         
35547     },
35548     
35549     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35550     {
35551         var pos = [];
35552         
35553         if(box[0].size == 'xs'){
35554             
35555             pos.push({
35556                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35557                 y : minY
35558             });
35559
35560             pos.push({
35561                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35562                 y : minY
35563             });
35564             
35565             pos.push({
35566                 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),
35567                 y : minY
35568             });
35569             
35570             pos.push({
35571                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35572                 y : minY + (this.unitWidth + this.gutter) * 1
35573             });
35574             
35575             return pos;
35576             
35577         }
35578         
35579         pos.push({
35580             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35581             y : minY
35582         });
35583         
35584         pos.push({
35585             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35586             y : minY + (this.unitWidth + this.gutter) * 2
35587         });
35588         
35589         pos.push({
35590             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35591             y : minY + (this.unitWidth + this.gutter) * 2
35592         });
35593         
35594         pos.push({
35595             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),
35596             y : minY + (this.unitWidth + this.gutter) * 2
35597         });
35598
35599         return pos;
35600         
35601     },
35602     
35603     /**
35604     * remove a Masonry Brick
35605     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35606     */
35607     removeBrick : function(brick_id)
35608     {
35609         if (!brick_id) {
35610             return;
35611         }
35612         
35613         for (var i = 0; i<this.bricks.length; i++) {
35614             if (this.bricks[i].id == brick_id) {
35615                 this.bricks.splice(i,1);
35616                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35617                 this.initial();
35618             }
35619         }
35620     },
35621     
35622     /**
35623     * adds a Masonry Brick
35624     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35625     */
35626     addBrick : function(cfg)
35627     {
35628         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35629         //this.register(cn);
35630         cn.parentId = this.id;
35631         cn.render(this.el);
35632         return cn;
35633     },
35634     
35635     /**
35636     * register a Masonry Brick
35637     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35638     */
35639     
35640     register : function(brick)
35641     {
35642         this.bricks.push(brick);
35643         brick.masonryId = this.id;
35644     },
35645     
35646     /**
35647     * clear all the Masonry Brick
35648     */
35649     clearAll : function()
35650     {
35651         this.bricks = [];
35652         //this.getChildContainer().dom.innerHTML = "";
35653         this.el.dom.innerHTML = '';
35654     },
35655     
35656     getSelected : function()
35657     {
35658         if (!this.selectedBrick) {
35659             return false;
35660         }
35661         
35662         return this.selectedBrick;
35663     }
35664 });
35665
35666 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35667     
35668     groups: {},
35669      /**
35670     * register a Masonry Layout
35671     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35672     */
35673     
35674     register : function(layout)
35675     {
35676         this.groups[layout.id] = layout;
35677     },
35678     /**
35679     * fetch a  Masonry Layout based on the masonry layout ID
35680     * @param {string} the masonry layout to add
35681     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35682     */
35683     
35684     get: function(layout_id) {
35685         if (typeof(this.groups[layout_id]) == 'undefined') {
35686             return false;
35687         }
35688         return this.groups[layout_id] ;
35689     }
35690     
35691     
35692     
35693 });
35694
35695  
35696
35697  /**
35698  *
35699  * This is based on 
35700  * http://masonry.desandro.com
35701  *
35702  * The idea is to render all the bricks based on vertical width...
35703  *
35704  * The original code extends 'outlayer' - we might need to use that....
35705  * 
35706  */
35707
35708
35709 /**
35710  * @class Roo.bootstrap.LayoutMasonryAuto
35711  * @extends Roo.bootstrap.Component
35712  * Bootstrap Layout Masonry class
35713  * 
35714  * @constructor
35715  * Create a new Element
35716  * @param {Object} config The config object
35717  */
35718
35719 Roo.bootstrap.LayoutMasonryAuto = function(config){
35720     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35721 };
35722
35723 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35724     
35725       /**
35726      * @cfg {Boolean} isFitWidth  - resize the width..
35727      */   
35728     isFitWidth : false,  // options..
35729     /**
35730      * @cfg {Boolean} isOriginLeft = left align?
35731      */   
35732     isOriginLeft : true,
35733     /**
35734      * @cfg {Boolean} isOriginTop = top align?
35735      */   
35736     isOriginTop : false,
35737     /**
35738      * @cfg {Boolean} isLayoutInstant = no animation?
35739      */   
35740     isLayoutInstant : false, // needed?
35741     /**
35742      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35743      */   
35744     isResizingContainer : true,
35745     /**
35746      * @cfg {Number} columnWidth  width of the columns 
35747      */   
35748     
35749     columnWidth : 0,
35750     
35751     /**
35752      * @cfg {Number} maxCols maximum number of columns
35753      */   
35754     
35755     maxCols: 0,
35756     /**
35757      * @cfg {Number} padHeight padding below box..
35758      */   
35759     
35760     padHeight : 10, 
35761     
35762     /**
35763      * @cfg {Boolean} isAutoInitial defalut true
35764      */   
35765     
35766     isAutoInitial : true, 
35767     
35768     // private?
35769     gutter : 0,
35770     
35771     containerWidth: 0,
35772     initialColumnWidth : 0,
35773     currentSize : null,
35774     
35775     colYs : null, // array.
35776     maxY : 0,
35777     padWidth: 10,
35778     
35779     
35780     tag: 'div',
35781     cls: '',
35782     bricks: null, //CompositeElement
35783     cols : 0, // array?
35784     // element : null, // wrapped now this.el
35785     _isLayoutInited : null, 
35786     
35787     
35788     getAutoCreate : function(){
35789         
35790         var cfg = {
35791             tag: this.tag,
35792             cls: 'blog-masonary-wrapper ' + this.cls,
35793             cn : {
35794                 cls : 'mas-boxes masonary'
35795             }
35796         };
35797         
35798         return cfg;
35799     },
35800     
35801     getChildContainer: function( )
35802     {
35803         if (this.boxesEl) {
35804             return this.boxesEl;
35805         }
35806         
35807         this.boxesEl = this.el.select('.mas-boxes').first();
35808         
35809         return this.boxesEl;
35810     },
35811     
35812     
35813     initEvents : function()
35814     {
35815         var _this = this;
35816         
35817         if(this.isAutoInitial){
35818             Roo.log('hook children rendered');
35819             this.on('childrenrendered', function() {
35820                 Roo.log('children rendered');
35821                 _this.initial();
35822             } ,this);
35823         }
35824         
35825     },
35826     
35827     initial : function()
35828     {
35829         this.reloadItems();
35830
35831         this.currentSize = this.el.getBox(true);
35832
35833         /// was window resize... - let's see if this works..
35834         Roo.EventManager.onWindowResize(this.resize, this); 
35835
35836         if(!this.isAutoInitial){
35837             this.layout();
35838             return;
35839         }
35840         
35841         this.layout.defer(500,this);
35842     },
35843     
35844     reloadItems: function()
35845     {
35846         this.bricks = this.el.select('.masonry-brick', true);
35847         
35848         this.bricks.each(function(b) {
35849             //Roo.log(b.getSize());
35850             if (!b.attr('originalwidth')) {
35851                 b.attr('originalwidth',  b.getSize().width);
35852             }
35853             
35854         });
35855         
35856         Roo.log(this.bricks.elements.length);
35857     },
35858     
35859     resize : function()
35860     {
35861         Roo.log('resize');
35862         var cs = this.el.getBox(true);
35863         
35864         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35865             Roo.log("no change in with or X");
35866             return;
35867         }
35868         this.currentSize = cs;
35869         this.layout();
35870     },
35871     
35872     layout : function()
35873     {
35874          Roo.log('layout');
35875         this._resetLayout();
35876         //this._manageStamps();
35877       
35878         // don't animate first layout
35879         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35880         this.layoutItems( isInstant );
35881       
35882         // flag for initalized
35883         this._isLayoutInited = true;
35884     },
35885     
35886     layoutItems : function( isInstant )
35887     {
35888         //var items = this._getItemsForLayout( this.items );
35889         // original code supports filtering layout items.. we just ignore it..
35890         
35891         this._layoutItems( this.bricks , isInstant );
35892       
35893         this._postLayout();
35894     },
35895     _layoutItems : function ( items , isInstant)
35896     {
35897        //this.fireEvent( 'layout', this, items );
35898     
35899
35900         if ( !items || !items.elements.length ) {
35901           // no items, emit event with empty array
35902             return;
35903         }
35904
35905         var queue = [];
35906         items.each(function(item) {
35907             Roo.log("layout item");
35908             Roo.log(item);
35909             // get x/y object from method
35910             var position = this._getItemLayoutPosition( item );
35911             // enqueue
35912             position.item = item;
35913             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35914             queue.push( position );
35915         }, this);
35916       
35917         this._processLayoutQueue( queue );
35918     },
35919     /** Sets position of item in DOM
35920     * @param {Element} item
35921     * @param {Number} x - horizontal position
35922     * @param {Number} y - vertical position
35923     * @param {Boolean} isInstant - disables transitions
35924     */
35925     _processLayoutQueue : function( queue )
35926     {
35927         for ( var i=0, len = queue.length; i < len; i++ ) {
35928             var obj = queue[i];
35929             obj.item.position('absolute');
35930             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35931         }
35932     },
35933       
35934     
35935     /**
35936     * Any logic you want to do after each layout,
35937     * i.e. size the container
35938     */
35939     _postLayout : function()
35940     {
35941         this.resizeContainer();
35942     },
35943     
35944     resizeContainer : function()
35945     {
35946         if ( !this.isResizingContainer ) {
35947             return;
35948         }
35949         var size = this._getContainerSize();
35950         if ( size ) {
35951             this.el.setSize(size.width,size.height);
35952             this.boxesEl.setSize(size.width,size.height);
35953         }
35954     },
35955     
35956     
35957     
35958     _resetLayout : function()
35959     {
35960         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35961         this.colWidth = this.el.getWidth();
35962         //this.gutter = this.el.getWidth(); 
35963         
35964         this.measureColumns();
35965
35966         // reset column Y
35967         var i = this.cols;
35968         this.colYs = [];
35969         while (i--) {
35970             this.colYs.push( 0 );
35971         }
35972     
35973         this.maxY = 0;
35974     },
35975
35976     measureColumns : function()
35977     {
35978         this.getContainerWidth();
35979       // if columnWidth is 0, default to outerWidth of first item
35980         if ( !this.columnWidth ) {
35981             var firstItem = this.bricks.first();
35982             Roo.log(firstItem);
35983             this.columnWidth  = this.containerWidth;
35984             if (firstItem && firstItem.attr('originalwidth') ) {
35985                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35986             }
35987             // columnWidth fall back to item of first element
35988             Roo.log("set column width?");
35989                         this.initialColumnWidth = this.columnWidth  ;
35990
35991             // if first elem has no width, default to size of container
35992             
35993         }
35994         
35995         
35996         if (this.initialColumnWidth) {
35997             this.columnWidth = this.initialColumnWidth;
35998         }
35999         
36000         
36001             
36002         // column width is fixed at the top - however if container width get's smaller we should
36003         // reduce it...
36004         
36005         // this bit calcs how man columns..
36006             
36007         var columnWidth = this.columnWidth += this.gutter;
36008       
36009         // calculate columns
36010         var containerWidth = this.containerWidth + this.gutter;
36011         
36012         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36013         // fix rounding errors, typically with gutters
36014         var excess = columnWidth - containerWidth % columnWidth;
36015         
36016         
36017         // if overshoot is less than a pixel, round up, otherwise floor it
36018         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36019         cols = Math[ mathMethod ]( cols );
36020         this.cols = Math.max( cols, 1 );
36021         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36022         
36023          // padding positioning..
36024         var totalColWidth = this.cols * this.columnWidth;
36025         var padavail = this.containerWidth - totalColWidth;
36026         // so for 2 columns - we need 3 'pads'
36027         
36028         var padNeeded = (1+this.cols) * this.padWidth;
36029         
36030         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36031         
36032         this.columnWidth += padExtra
36033         //this.padWidth = Math.floor(padavail /  ( this.cols));
36034         
36035         // adjust colum width so that padding is fixed??
36036         
36037         // we have 3 columns ... total = width * 3
36038         // we have X left over... that should be used by 
36039         
36040         //if (this.expandC) {
36041             
36042         //}
36043         
36044         
36045         
36046     },
36047     
36048     getContainerWidth : function()
36049     {
36050        /* // container is parent if fit width
36051         var container = this.isFitWidth ? this.element.parentNode : this.element;
36052         // check that this.size and size are there
36053         // IE8 triggers resize on body size change, so they might not be
36054         
36055         var size = getSize( container );  //FIXME
36056         this.containerWidth = size && size.innerWidth; //FIXME
36057         */
36058          
36059         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36060         
36061     },
36062     
36063     _getItemLayoutPosition : function( item )  // what is item?
36064     {
36065         // we resize the item to our columnWidth..
36066       
36067         item.setWidth(this.columnWidth);
36068         item.autoBoxAdjust  = false;
36069         
36070         var sz = item.getSize();
36071  
36072         // how many columns does this brick span
36073         var remainder = this.containerWidth % this.columnWidth;
36074         
36075         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36076         // round if off by 1 pixel, otherwise use ceil
36077         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36078         colSpan = Math.min( colSpan, this.cols );
36079         
36080         // normally this should be '1' as we dont' currently allow multi width columns..
36081         
36082         var colGroup = this._getColGroup( colSpan );
36083         // get the minimum Y value from the columns
36084         var minimumY = Math.min.apply( Math, colGroup );
36085         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36086         
36087         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36088          
36089         // position the brick
36090         var position = {
36091             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36092             y: this.currentSize.y + minimumY + this.padHeight
36093         };
36094         
36095         Roo.log(position);
36096         // apply setHeight to necessary columns
36097         var setHeight = minimumY + sz.height + this.padHeight;
36098         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36099         
36100         var setSpan = this.cols + 1 - colGroup.length;
36101         for ( var i = 0; i < setSpan; i++ ) {
36102           this.colYs[ shortColIndex + i ] = setHeight ;
36103         }
36104       
36105         return position;
36106     },
36107     
36108     /**
36109      * @param {Number} colSpan - number of columns the element spans
36110      * @returns {Array} colGroup
36111      */
36112     _getColGroup : function( colSpan )
36113     {
36114         if ( colSpan < 2 ) {
36115           // if brick spans only one column, use all the column Ys
36116           return this.colYs;
36117         }
36118       
36119         var colGroup = [];
36120         // how many different places could this brick fit horizontally
36121         var groupCount = this.cols + 1 - colSpan;
36122         // for each group potential horizontal position
36123         for ( var i = 0; i < groupCount; i++ ) {
36124           // make an array of colY values for that one group
36125           var groupColYs = this.colYs.slice( i, i + colSpan );
36126           // and get the max value of the array
36127           colGroup[i] = Math.max.apply( Math, groupColYs );
36128         }
36129         return colGroup;
36130     },
36131     /*
36132     _manageStamp : function( stamp )
36133     {
36134         var stampSize =  stamp.getSize();
36135         var offset = stamp.getBox();
36136         // get the columns that this stamp affects
36137         var firstX = this.isOriginLeft ? offset.x : offset.right;
36138         var lastX = firstX + stampSize.width;
36139         var firstCol = Math.floor( firstX / this.columnWidth );
36140         firstCol = Math.max( 0, firstCol );
36141         
36142         var lastCol = Math.floor( lastX / this.columnWidth );
36143         // lastCol should not go over if multiple of columnWidth #425
36144         lastCol -= lastX % this.columnWidth ? 0 : 1;
36145         lastCol = Math.min( this.cols - 1, lastCol );
36146         
36147         // set colYs to bottom of the stamp
36148         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36149             stampSize.height;
36150             
36151         for ( var i = firstCol; i <= lastCol; i++ ) {
36152           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36153         }
36154     },
36155     */
36156     
36157     _getContainerSize : function()
36158     {
36159         this.maxY = Math.max.apply( Math, this.colYs );
36160         var size = {
36161             height: this.maxY
36162         };
36163       
36164         if ( this.isFitWidth ) {
36165             size.width = this._getContainerFitWidth();
36166         }
36167       
36168         return size;
36169     },
36170     
36171     _getContainerFitWidth : function()
36172     {
36173         var unusedCols = 0;
36174         // count unused columns
36175         var i = this.cols;
36176         while ( --i ) {
36177           if ( this.colYs[i] !== 0 ) {
36178             break;
36179           }
36180           unusedCols++;
36181         }
36182         // fit container to columns that have been used
36183         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36184     },
36185     
36186     needsResizeLayout : function()
36187     {
36188         var previousWidth = this.containerWidth;
36189         this.getContainerWidth();
36190         return previousWidth !== this.containerWidth;
36191     }
36192  
36193 });
36194
36195  
36196
36197  /*
36198  * - LGPL
36199  *
36200  * element
36201  * 
36202  */
36203
36204 /**
36205  * @class Roo.bootstrap.MasonryBrick
36206  * @extends Roo.bootstrap.Component
36207  * Bootstrap MasonryBrick class
36208  * 
36209  * @constructor
36210  * Create a new MasonryBrick
36211  * @param {Object} config The config object
36212  */
36213
36214 Roo.bootstrap.MasonryBrick = function(config){
36215     
36216     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36217     
36218     Roo.bootstrap.MasonryBrick.register(this);
36219     
36220     this.addEvents({
36221         // raw events
36222         /**
36223          * @event click
36224          * When a MasonryBrick is clcik
36225          * @param {Roo.bootstrap.MasonryBrick} this
36226          * @param {Roo.EventObject} e
36227          */
36228         "click" : true
36229     });
36230 };
36231
36232 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36233     
36234     /**
36235      * @cfg {String} title
36236      */   
36237     title : '',
36238     /**
36239      * @cfg {String} html
36240      */   
36241     html : '',
36242     /**
36243      * @cfg {String} bgimage
36244      */   
36245     bgimage : '',
36246     /**
36247      * @cfg {String} videourl
36248      */   
36249     videourl : '',
36250     /**
36251      * @cfg {String} cls
36252      */   
36253     cls : '',
36254     /**
36255      * @cfg {String} href
36256      */   
36257     href : '',
36258     /**
36259      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36260      */   
36261     size : 'xs',
36262     
36263     /**
36264      * @cfg {String} placetitle (center|bottom)
36265      */   
36266     placetitle : '',
36267     
36268     /**
36269      * @cfg {Boolean} isFitContainer defalut true
36270      */   
36271     isFitContainer : true, 
36272     
36273     /**
36274      * @cfg {Boolean} preventDefault defalut false
36275      */   
36276     preventDefault : false, 
36277     
36278     /**
36279      * @cfg {Boolean} inverse defalut false
36280      */   
36281     maskInverse : false, 
36282     
36283     getAutoCreate : function()
36284     {
36285         if(!this.isFitContainer){
36286             return this.getSplitAutoCreate();
36287         }
36288         
36289         var cls = 'masonry-brick masonry-brick-full';
36290         
36291         if(this.href.length){
36292             cls += ' masonry-brick-link';
36293         }
36294         
36295         if(this.bgimage.length){
36296             cls += ' masonry-brick-image';
36297         }
36298         
36299         if(this.maskInverse){
36300             cls += ' mask-inverse';
36301         }
36302         
36303         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36304             cls += ' enable-mask';
36305         }
36306         
36307         if(this.size){
36308             cls += ' masonry-' + this.size + '-brick';
36309         }
36310         
36311         if(this.placetitle.length){
36312             
36313             switch (this.placetitle) {
36314                 case 'center' :
36315                     cls += ' masonry-center-title';
36316                     break;
36317                 case 'bottom' :
36318                     cls += ' masonry-bottom-title';
36319                     break;
36320                 default:
36321                     break;
36322             }
36323             
36324         } else {
36325             if(!this.html.length && !this.bgimage.length){
36326                 cls += ' masonry-center-title';
36327             }
36328
36329             if(!this.html.length && this.bgimage.length){
36330                 cls += ' masonry-bottom-title';
36331             }
36332         }
36333         
36334         if(this.cls){
36335             cls += ' ' + this.cls;
36336         }
36337         
36338         var cfg = {
36339             tag: (this.href.length) ? 'a' : 'div',
36340             cls: cls,
36341             cn: [
36342                 {
36343                     tag: 'div',
36344                     cls: 'masonry-brick-mask'
36345                 },
36346                 {
36347                     tag: 'div',
36348                     cls: 'masonry-brick-paragraph',
36349                     cn: []
36350                 }
36351             ]
36352         };
36353         
36354         if(this.href.length){
36355             cfg.href = this.href;
36356         }
36357         
36358         var cn = cfg.cn[1].cn;
36359         
36360         if(this.title.length){
36361             cn.push({
36362                 tag: 'h4',
36363                 cls: 'masonry-brick-title',
36364                 html: this.title
36365             });
36366         }
36367         
36368         if(this.html.length){
36369             cn.push({
36370                 tag: 'p',
36371                 cls: 'masonry-brick-text',
36372                 html: this.html
36373             });
36374         }
36375         
36376         if (!this.title.length && !this.html.length) {
36377             cfg.cn[1].cls += ' hide';
36378         }
36379         
36380         if(this.bgimage.length){
36381             cfg.cn.push({
36382                 tag: 'img',
36383                 cls: 'masonry-brick-image-view',
36384                 src: this.bgimage
36385             });
36386         }
36387         
36388         if(this.videourl.length){
36389             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36390             // youtube support only?
36391             cfg.cn.push({
36392                 tag: 'iframe',
36393                 cls: 'masonry-brick-image-view',
36394                 src: vurl,
36395                 frameborder : 0,
36396                 allowfullscreen : true
36397             });
36398         }
36399         
36400         return cfg;
36401         
36402     },
36403     
36404     getSplitAutoCreate : function()
36405     {
36406         var cls = 'masonry-brick masonry-brick-split';
36407         
36408         if(this.href.length){
36409             cls += ' masonry-brick-link';
36410         }
36411         
36412         if(this.bgimage.length){
36413             cls += ' masonry-brick-image';
36414         }
36415         
36416         if(this.size){
36417             cls += ' masonry-' + this.size + '-brick';
36418         }
36419         
36420         switch (this.placetitle) {
36421             case 'center' :
36422                 cls += ' masonry-center-title';
36423                 break;
36424             case 'bottom' :
36425                 cls += ' masonry-bottom-title';
36426                 break;
36427             default:
36428                 if(!this.bgimage.length){
36429                     cls += ' masonry-center-title';
36430                 }
36431
36432                 if(this.bgimage.length){
36433                     cls += ' masonry-bottom-title';
36434                 }
36435                 break;
36436         }
36437         
36438         if(this.cls){
36439             cls += ' ' + this.cls;
36440         }
36441         
36442         var cfg = {
36443             tag: (this.href.length) ? 'a' : 'div',
36444             cls: cls,
36445             cn: [
36446                 {
36447                     tag: 'div',
36448                     cls: 'masonry-brick-split-head',
36449                     cn: [
36450                         {
36451                             tag: 'div',
36452                             cls: 'masonry-brick-paragraph',
36453                             cn: []
36454                         }
36455                     ]
36456                 },
36457                 {
36458                     tag: 'div',
36459                     cls: 'masonry-brick-split-body',
36460                     cn: []
36461                 }
36462             ]
36463         };
36464         
36465         if(this.href.length){
36466             cfg.href = this.href;
36467         }
36468         
36469         if(this.title.length){
36470             cfg.cn[0].cn[0].cn.push({
36471                 tag: 'h4',
36472                 cls: 'masonry-brick-title',
36473                 html: this.title
36474             });
36475         }
36476         
36477         if(this.html.length){
36478             cfg.cn[1].cn.push({
36479                 tag: 'p',
36480                 cls: 'masonry-brick-text',
36481                 html: this.html
36482             });
36483         }
36484
36485         if(this.bgimage.length){
36486             cfg.cn[0].cn.push({
36487                 tag: 'img',
36488                 cls: 'masonry-brick-image-view',
36489                 src: this.bgimage
36490             });
36491         }
36492         
36493         if(this.videourl.length){
36494             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36495             // youtube support only?
36496             cfg.cn[0].cn.cn.push({
36497                 tag: 'iframe',
36498                 cls: 'masonry-brick-image-view',
36499                 src: vurl,
36500                 frameborder : 0,
36501                 allowfullscreen : true
36502             });
36503         }
36504         
36505         return cfg;
36506     },
36507     
36508     initEvents: function() 
36509     {
36510         switch (this.size) {
36511             case 'xs' :
36512                 this.x = 1;
36513                 this.y = 1;
36514                 break;
36515             case 'sm' :
36516                 this.x = 2;
36517                 this.y = 2;
36518                 break;
36519             case 'md' :
36520             case 'md-left' :
36521             case 'md-right' :
36522                 this.x = 3;
36523                 this.y = 3;
36524                 break;
36525             case 'tall' :
36526                 this.x = 2;
36527                 this.y = 3;
36528                 break;
36529             case 'wide' :
36530                 this.x = 3;
36531                 this.y = 2;
36532                 break;
36533             case 'wide-thin' :
36534                 this.x = 3;
36535                 this.y = 1;
36536                 break;
36537                         
36538             default :
36539                 break;
36540         }
36541         
36542         if(Roo.isTouch){
36543             this.el.on('touchstart', this.onTouchStart, this);
36544             this.el.on('touchmove', this.onTouchMove, this);
36545             this.el.on('touchend', this.onTouchEnd, this);
36546             this.el.on('contextmenu', this.onContextMenu, this);
36547         } else {
36548             this.el.on('mouseenter'  ,this.enter, this);
36549             this.el.on('mouseleave', this.leave, this);
36550             this.el.on('click', this.onClick, this);
36551         }
36552         
36553         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36554             this.parent().bricks.push(this);   
36555         }
36556         
36557     },
36558     
36559     onClick: function(e, el)
36560     {
36561         var time = this.endTimer - this.startTimer;
36562         // Roo.log(e.preventDefault());
36563         if(Roo.isTouch){
36564             if(time > 1000){
36565                 e.preventDefault();
36566                 return;
36567             }
36568         }
36569         
36570         if(!this.preventDefault){
36571             return;
36572         }
36573         
36574         e.preventDefault();
36575         
36576         if (this.activeClass != '') {
36577             this.selectBrick();
36578         }
36579         
36580         this.fireEvent('click', this, e);
36581     },
36582     
36583     enter: function(e, el)
36584     {
36585         e.preventDefault();
36586         
36587         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36588             return;
36589         }
36590         
36591         if(this.bgimage.length && this.html.length){
36592             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36593         }
36594     },
36595     
36596     leave: function(e, el)
36597     {
36598         e.preventDefault();
36599         
36600         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36601             return;
36602         }
36603         
36604         if(this.bgimage.length && this.html.length){
36605             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36606         }
36607     },
36608     
36609     onTouchStart: function(e, el)
36610     {
36611 //        e.preventDefault();
36612         
36613         this.touchmoved = false;
36614         
36615         if(!this.isFitContainer){
36616             return;
36617         }
36618         
36619         if(!this.bgimage.length || !this.html.length){
36620             return;
36621         }
36622         
36623         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36624         
36625         this.timer = new Date().getTime();
36626         
36627     },
36628     
36629     onTouchMove: function(e, el)
36630     {
36631         this.touchmoved = true;
36632     },
36633     
36634     onContextMenu : function(e,el)
36635     {
36636         e.preventDefault();
36637         e.stopPropagation();
36638         return false;
36639     },
36640     
36641     onTouchEnd: function(e, el)
36642     {
36643 //        e.preventDefault();
36644         
36645         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36646         
36647             this.leave(e,el);
36648             
36649             return;
36650         }
36651         
36652         if(!this.bgimage.length || !this.html.length){
36653             
36654             if(this.href.length){
36655                 window.location.href = this.href;
36656             }
36657             
36658             return;
36659         }
36660         
36661         if(!this.isFitContainer){
36662             return;
36663         }
36664         
36665         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36666         
36667         window.location.href = this.href;
36668     },
36669     
36670     //selection on single brick only
36671     selectBrick : function() {
36672         
36673         if (!this.parentId) {
36674             return;
36675         }
36676         
36677         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36678         var index = m.selectedBrick.indexOf(this.id);
36679         
36680         if ( index > -1) {
36681             m.selectedBrick.splice(index,1);
36682             this.el.removeClass(this.activeClass);
36683             return;
36684         }
36685         
36686         for(var i = 0; i < m.selectedBrick.length; i++) {
36687             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36688             b.el.removeClass(b.activeClass);
36689         }
36690         
36691         m.selectedBrick = [];
36692         
36693         m.selectedBrick.push(this.id);
36694         this.el.addClass(this.activeClass);
36695         return;
36696     },
36697     
36698     isSelected : function(){
36699         return this.el.hasClass(this.activeClass);
36700         
36701     }
36702 });
36703
36704 Roo.apply(Roo.bootstrap.MasonryBrick, {
36705     
36706     //groups: {},
36707     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36708      /**
36709     * register a Masonry Brick
36710     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36711     */
36712     
36713     register : function(brick)
36714     {
36715         //this.groups[brick.id] = brick;
36716         this.groups.add(brick.id, brick);
36717     },
36718     /**
36719     * fetch a  masonry brick based on the masonry brick ID
36720     * @param {string} the masonry brick to add
36721     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36722     */
36723     
36724     get: function(brick_id) 
36725     {
36726         // if (typeof(this.groups[brick_id]) == 'undefined') {
36727         //     return false;
36728         // }
36729         // return this.groups[brick_id] ;
36730         
36731         if(this.groups.key(brick_id)) {
36732             return this.groups.key(brick_id);
36733         }
36734         
36735         return false;
36736     }
36737     
36738     
36739     
36740 });
36741
36742  /*
36743  * - LGPL
36744  *
36745  * element
36746  * 
36747  */
36748
36749 /**
36750  * @class Roo.bootstrap.Brick
36751  * @extends Roo.bootstrap.Component
36752  * Bootstrap Brick class
36753  * 
36754  * @constructor
36755  * Create a new Brick
36756  * @param {Object} config The config object
36757  */
36758
36759 Roo.bootstrap.Brick = function(config){
36760     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36761     
36762     this.addEvents({
36763         // raw events
36764         /**
36765          * @event click
36766          * When a Brick is click
36767          * @param {Roo.bootstrap.Brick} this
36768          * @param {Roo.EventObject} e
36769          */
36770         "click" : true
36771     });
36772 };
36773
36774 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36775     
36776     /**
36777      * @cfg {String} title
36778      */   
36779     title : '',
36780     /**
36781      * @cfg {String} html
36782      */   
36783     html : '',
36784     /**
36785      * @cfg {String} bgimage
36786      */   
36787     bgimage : '',
36788     /**
36789      * @cfg {String} cls
36790      */   
36791     cls : '',
36792     /**
36793      * @cfg {String} href
36794      */   
36795     href : '',
36796     /**
36797      * @cfg {String} video
36798      */   
36799     video : '',
36800     /**
36801      * @cfg {Boolean} square
36802      */   
36803     square : true,
36804     
36805     getAutoCreate : function()
36806     {
36807         var cls = 'roo-brick';
36808         
36809         if(this.href.length){
36810             cls += ' roo-brick-link';
36811         }
36812         
36813         if(this.bgimage.length){
36814             cls += ' roo-brick-image';
36815         }
36816         
36817         if(!this.html.length && !this.bgimage.length){
36818             cls += ' roo-brick-center-title';
36819         }
36820         
36821         if(!this.html.length && this.bgimage.length){
36822             cls += ' roo-brick-bottom-title';
36823         }
36824         
36825         if(this.cls){
36826             cls += ' ' + this.cls;
36827         }
36828         
36829         var cfg = {
36830             tag: (this.href.length) ? 'a' : 'div',
36831             cls: cls,
36832             cn: [
36833                 {
36834                     tag: 'div',
36835                     cls: 'roo-brick-paragraph',
36836                     cn: []
36837                 }
36838             ]
36839         };
36840         
36841         if(this.href.length){
36842             cfg.href = this.href;
36843         }
36844         
36845         var cn = cfg.cn[0].cn;
36846         
36847         if(this.title.length){
36848             cn.push({
36849                 tag: 'h4',
36850                 cls: 'roo-brick-title',
36851                 html: this.title
36852             });
36853         }
36854         
36855         if(this.html.length){
36856             cn.push({
36857                 tag: 'p',
36858                 cls: 'roo-brick-text',
36859                 html: this.html
36860             });
36861         } else {
36862             cn.cls += ' hide';
36863         }
36864         
36865         if(this.bgimage.length){
36866             cfg.cn.push({
36867                 tag: 'img',
36868                 cls: 'roo-brick-image-view',
36869                 src: this.bgimage
36870             });
36871         }
36872         
36873         return cfg;
36874     },
36875     
36876     initEvents: function() 
36877     {
36878         if(this.title.length || this.html.length){
36879             this.el.on('mouseenter'  ,this.enter, this);
36880             this.el.on('mouseleave', this.leave, this);
36881         }
36882         
36883         Roo.EventManager.onWindowResize(this.resize, this); 
36884         
36885         if(this.bgimage.length){
36886             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36887             this.imageEl.on('load', this.onImageLoad, this);
36888             return;
36889         }
36890         
36891         this.resize();
36892     },
36893     
36894     onImageLoad : function()
36895     {
36896         this.resize();
36897     },
36898     
36899     resize : function()
36900     {
36901         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36902         
36903         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36904         
36905         if(this.bgimage.length){
36906             var image = this.el.select('.roo-brick-image-view', true).first();
36907             
36908             image.setWidth(paragraph.getWidth());
36909             
36910             if(this.square){
36911                 image.setHeight(paragraph.getWidth());
36912             }
36913             
36914             this.el.setHeight(image.getHeight());
36915             paragraph.setHeight(image.getHeight());
36916             
36917         }
36918         
36919     },
36920     
36921     enter: function(e, el)
36922     {
36923         e.preventDefault();
36924         
36925         if(this.bgimage.length){
36926             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36927             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36928         }
36929     },
36930     
36931     leave: function(e, el)
36932     {
36933         e.preventDefault();
36934         
36935         if(this.bgimage.length){
36936             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36937             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36938         }
36939     }
36940     
36941 });
36942
36943  
36944
36945  /*
36946  * - LGPL
36947  *
36948  * Number field 
36949  */
36950
36951 /**
36952  * @class Roo.bootstrap.NumberField
36953  * @extends Roo.bootstrap.Input
36954  * Bootstrap NumberField class
36955  * 
36956  * 
36957  * 
36958  * 
36959  * @constructor
36960  * Create a new NumberField
36961  * @param {Object} config The config object
36962  */
36963
36964 Roo.bootstrap.NumberField = function(config){
36965     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36966 };
36967
36968 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36969     
36970     /**
36971      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36972      */
36973     allowDecimals : true,
36974     /**
36975      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36976      */
36977     decimalSeparator : ".",
36978     /**
36979      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36980      */
36981     decimalPrecision : 2,
36982     /**
36983      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36984      */
36985     allowNegative : true,
36986     
36987     /**
36988      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36989      */
36990     allowZero: true,
36991     /**
36992      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36993      */
36994     minValue : Number.NEGATIVE_INFINITY,
36995     /**
36996      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36997      */
36998     maxValue : Number.MAX_VALUE,
36999     /**
37000      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37001      */
37002     minText : "The minimum value for this field is {0}",
37003     /**
37004      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37005      */
37006     maxText : "The maximum value for this field is {0}",
37007     /**
37008      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37009      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37010      */
37011     nanText : "{0} is not a valid number",
37012     /**
37013      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37014      */
37015     thousandsDelimiter : false,
37016     /**
37017      * @cfg {String} valueAlign alignment of value
37018      */
37019     valueAlign : "left",
37020
37021     getAutoCreate : function()
37022     {
37023         var hiddenInput = {
37024             tag: 'input',
37025             type: 'hidden',
37026             id: Roo.id(),
37027             cls: 'hidden-number-input'
37028         };
37029         
37030         if (this.name) {
37031             hiddenInput.name = this.name;
37032         }
37033         
37034         this.name = '';
37035         
37036         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37037         
37038         this.name = hiddenInput.name;
37039         
37040         if(cfg.cn.length > 0) {
37041             cfg.cn.push(hiddenInput);
37042         }
37043         
37044         return cfg;
37045     },
37046
37047     // private
37048     initEvents : function()
37049     {   
37050         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37051         
37052         var allowed = "0123456789";
37053         
37054         if(this.allowDecimals){
37055             allowed += this.decimalSeparator;
37056         }
37057         
37058         if(this.allowNegative){
37059             allowed += "-";
37060         }
37061         
37062         if(this.thousandsDelimiter) {
37063             allowed += ",";
37064         }
37065         
37066         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37067         
37068         var keyPress = function(e){
37069             
37070             var k = e.getKey();
37071             
37072             var c = e.getCharCode();
37073             
37074             if(
37075                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37076                     allowed.indexOf(String.fromCharCode(c)) === -1
37077             ){
37078                 e.stopEvent();
37079                 return;
37080             }
37081             
37082             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37083                 return;
37084             }
37085             
37086             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37087                 e.stopEvent();
37088             }
37089         };
37090         
37091         this.el.on("keypress", keyPress, this);
37092     },
37093     
37094     validateValue : function(value)
37095     {
37096         
37097         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37098             return false;
37099         }
37100         
37101         var num = this.parseValue(value);
37102         
37103         if(isNaN(num)){
37104             this.markInvalid(String.format(this.nanText, value));
37105             return false;
37106         }
37107         
37108         if(num < this.minValue){
37109             this.markInvalid(String.format(this.minText, this.minValue));
37110             return false;
37111         }
37112         
37113         if(num > this.maxValue){
37114             this.markInvalid(String.format(this.maxText, this.maxValue));
37115             return false;
37116         }
37117         
37118         return true;
37119     },
37120
37121     getValue : function()
37122     {
37123         var v = this.hiddenEl().getValue();
37124         
37125         return this.fixPrecision(this.parseValue(v));
37126     },
37127
37128     parseValue : function(value)
37129     {
37130         if(this.thousandsDelimiter) {
37131             value += "";
37132             r = new RegExp(",", "g");
37133             value = value.replace(r, "");
37134         }
37135         
37136         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37137         return isNaN(value) ? '' : value;
37138     },
37139
37140     fixPrecision : function(value)
37141     {
37142         if(this.thousandsDelimiter) {
37143             value += "";
37144             r = new RegExp(",", "g");
37145             value = value.replace(r, "");
37146         }
37147         
37148         var nan = isNaN(value);
37149         
37150         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37151             return nan ? '' : value;
37152         }
37153         return parseFloat(value).toFixed(this.decimalPrecision);
37154     },
37155
37156     setValue : function(v)
37157     {
37158         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37159         
37160         this.value = v;
37161         
37162         if(this.rendered){
37163             
37164             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37165             
37166             this.inputEl().dom.value = (v == '') ? '' :
37167                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37168             
37169             if(!this.allowZero && v === '0') {
37170                 this.hiddenEl().dom.value = '';
37171                 this.inputEl().dom.value = '';
37172             }
37173             
37174             this.validate();
37175         }
37176     },
37177
37178     decimalPrecisionFcn : function(v)
37179     {
37180         return Math.floor(v);
37181     },
37182
37183     beforeBlur : function()
37184     {
37185         var v = this.parseValue(this.getRawValue());
37186         
37187         if(v || v === 0 || v === ''){
37188             this.setValue(v);
37189         }
37190     },
37191     
37192     hiddenEl : function()
37193     {
37194         return this.el.select('input.hidden-number-input',true).first();
37195     }
37196     
37197 });
37198
37199  
37200
37201 /*
37202 * Licence: LGPL
37203 */
37204
37205 /**
37206  * @class Roo.bootstrap.DocumentSlider
37207  * @extends Roo.bootstrap.Component
37208  * Bootstrap DocumentSlider class
37209  * 
37210  * @constructor
37211  * Create a new DocumentViewer
37212  * @param {Object} config The config object
37213  */
37214
37215 Roo.bootstrap.DocumentSlider = function(config){
37216     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37217     
37218     this.files = [];
37219     
37220     this.addEvents({
37221         /**
37222          * @event initial
37223          * Fire after initEvent
37224          * @param {Roo.bootstrap.DocumentSlider} this
37225          */
37226         "initial" : true,
37227         /**
37228          * @event update
37229          * Fire after update
37230          * @param {Roo.bootstrap.DocumentSlider} this
37231          */
37232         "update" : true,
37233         /**
37234          * @event click
37235          * Fire after click
37236          * @param {Roo.bootstrap.DocumentSlider} this
37237          */
37238         "click" : true
37239     });
37240 };
37241
37242 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37243     
37244     files : false,
37245     
37246     indicator : 0,
37247     
37248     getAutoCreate : function()
37249     {
37250         var cfg = {
37251             tag : 'div',
37252             cls : 'roo-document-slider',
37253             cn : [
37254                 {
37255                     tag : 'div',
37256                     cls : 'roo-document-slider-header',
37257                     cn : [
37258                         {
37259                             tag : 'div',
37260                             cls : 'roo-document-slider-header-title'
37261                         }
37262                     ]
37263                 },
37264                 {
37265                     tag : 'div',
37266                     cls : 'roo-document-slider-body',
37267                     cn : [
37268                         {
37269                             tag : 'div',
37270                             cls : 'roo-document-slider-prev',
37271                             cn : [
37272                                 {
37273                                     tag : 'i',
37274                                     cls : 'fa fa-chevron-left'
37275                                 }
37276                             ]
37277                         },
37278                         {
37279                             tag : 'div',
37280                             cls : 'roo-document-slider-thumb',
37281                             cn : [
37282                                 {
37283                                     tag : 'img',
37284                                     cls : 'roo-document-slider-image'
37285                                 }
37286                             ]
37287                         },
37288                         {
37289                             tag : 'div',
37290                             cls : 'roo-document-slider-next',
37291                             cn : [
37292                                 {
37293                                     tag : 'i',
37294                                     cls : 'fa fa-chevron-right'
37295                                 }
37296                             ]
37297                         }
37298                     ]
37299                 }
37300             ]
37301         };
37302         
37303         return cfg;
37304     },
37305     
37306     initEvents : function()
37307     {
37308         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37309         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37310         
37311         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37312         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37313         
37314         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37315         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37316         
37317         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37318         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37319         
37320         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37321         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37322         
37323         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37324         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37325         
37326         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37327         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37328         
37329         this.thumbEl.on('click', this.onClick, this);
37330         
37331         this.prevIndicator.on('click', this.prev, this);
37332         
37333         this.nextIndicator.on('click', this.next, this);
37334         
37335     },
37336     
37337     initial : function()
37338     {
37339         if(this.files.length){
37340             this.indicator = 1;
37341             this.update()
37342         }
37343         
37344         this.fireEvent('initial', this);
37345     },
37346     
37347     update : function()
37348     {
37349         this.imageEl.attr('src', this.files[this.indicator - 1]);
37350         
37351         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37352         
37353         this.prevIndicator.show();
37354         
37355         if(this.indicator == 1){
37356             this.prevIndicator.hide();
37357         }
37358         
37359         this.nextIndicator.show();
37360         
37361         if(this.indicator == this.files.length){
37362             this.nextIndicator.hide();
37363         }
37364         
37365         this.thumbEl.scrollTo('top');
37366         
37367         this.fireEvent('update', this);
37368     },
37369     
37370     onClick : function(e)
37371     {
37372         e.preventDefault();
37373         
37374         this.fireEvent('click', this);
37375     },
37376     
37377     prev : function(e)
37378     {
37379         e.preventDefault();
37380         
37381         this.indicator = Math.max(1, this.indicator - 1);
37382         
37383         this.update();
37384     },
37385     
37386     next : function(e)
37387     {
37388         e.preventDefault();
37389         
37390         this.indicator = Math.min(this.files.length, this.indicator + 1);
37391         
37392         this.update();
37393     }
37394 });
37395 /*
37396  * - LGPL
37397  *
37398  * RadioSet
37399  *
37400  *
37401  */
37402
37403 /**
37404  * @class Roo.bootstrap.RadioSet
37405  * @extends Roo.bootstrap.Input
37406  * Bootstrap RadioSet class
37407  * @cfg {String} indicatorpos (left|right) default left
37408  * @cfg {Boolean} inline (true|false) inline the element (default true)
37409  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37410  * @constructor
37411  * Create a new RadioSet
37412  * @param {Object} config The config object
37413  */
37414
37415 Roo.bootstrap.RadioSet = function(config){
37416     
37417     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37418     
37419     this.radioes = [];
37420     
37421     Roo.bootstrap.RadioSet.register(this);
37422     
37423     this.addEvents({
37424         /**
37425         * @event check
37426         * Fires when the element is checked or unchecked.
37427         * @param {Roo.bootstrap.RadioSet} this This radio
37428         * @param {Roo.bootstrap.Radio} item The checked item
37429         */
37430        check : true,
37431        /**
37432         * @event click
37433         * Fires when the element is click.
37434         * @param {Roo.bootstrap.RadioSet} this This radio set
37435         * @param {Roo.bootstrap.Radio} item The checked item
37436         * @param {Roo.EventObject} e The event object
37437         */
37438        click : true
37439     });
37440     
37441 };
37442
37443 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37444
37445     radioes : false,
37446     
37447     inline : true,
37448     
37449     weight : '',
37450     
37451     indicatorpos : 'left',
37452     
37453     getAutoCreate : function()
37454     {
37455         var label = {
37456             tag : 'label',
37457             cls : 'roo-radio-set-label',
37458             cn : [
37459                 {
37460                     tag : 'span',
37461                     html : this.fieldLabel
37462                 }
37463             ]
37464         };
37465         if (Roo.bootstrap.version == 3) {
37466             
37467             
37468             if(this.indicatorpos == 'left'){
37469                 label.cn.unshift({
37470                     tag : 'i',
37471                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37472                     tooltip : 'This field is required'
37473                 });
37474             } else {
37475                 label.cn.push({
37476                     tag : 'i',
37477                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37478                     tooltip : 'This field is required'
37479                 });
37480             }
37481         }
37482         var items = {
37483             tag : 'div',
37484             cls : 'roo-radio-set-items'
37485         };
37486         
37487         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37488         
37489         if (align === 'left' && this.fieldLabel.length) {
37490             
37491             items = {
37492                 cls : "roo-radio-set-right", 
37493                 cn: [
37494                     items
37495                 ]
37496             };
37497             
37498             if(this.labelWidth > 12){
37499                 label.style = "width: " + this.labelWidth + 'px';
37500             }
37501             
37502             if(this.labelWidth < 13 && this.labelmd == 0){
37503                 this.labelmd = this.labelWidth;
37504             }
37505             
37506             if(this.labellg > 0){
37507                 label.cls += ' col-lg-' + this.labellg;
37508                 items.cls += ' col-lg-' + (12 - this.labellg);
37509             }
37510             
37511             if(this.labelmd > 0){
37512                 label.cls += ' col-md-' + this.labelmd;
37513                 items.cls += ' col-md-' + (12 - this.labelmd);
37514             }
37515             
37516             if(this.labelsm > 0){
37517                 label.cls += ' col-sm-' + this.labelsm;
37518                 items.cls += ' col-sm-' + (12 - this.labelsm);
37519             }
37520             
37521             if(this.labelxs > 0){
37522                 label.cls += ' col-xs-' + this.labelxs;
37523                 items.cls += ' col-xs-' + (12 - this.labelxs);
37524             }
37525         }
37526         
37527         var cfg = {
37528             tag : 'div',
37529             cls : 'roo-radio-set',
37530             cn : [
37531                 {
37532                     tag : 'input',
37533                     cls : 'roo-radio-set-input',
37534                     type : 'hidden',
37535                     name : this.name,
37536                     value : this.value ? this.value :  ''
37537                 },
37538                 label,
37539                 items
37540             ]
37541         };
37542         
37543         if(this.weight.length){
37544             cfg.cls += ' roo-radio-' + this.weight;
37545         }
37546         
37547         if(this.inline) {
37548             cfg.cls += ' roo-radio-set-inline';
37549         }
37550         
37551         var settings=this;
37552         ['xs','sm','md','lg'].map(function(size){
37553             if (settings[size]) {
37554                 cfg.cls += ' col-' + size + '-' + settings[size];
37555             }
37556         });
37557         
37558         return cfg;
37559         
37560     },
37561
37562     initEvents : function()
37563     {
37564         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37565         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37566         
37567         if(!this.fieldLabel.length){
37568             this.labelEl.hide();
37569         }
37570         
37571         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37572         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37573         
37574         this.indicator = this.indicatorEl();
37575         
37576         if(this.indicator){
37577             this.indicator.addClass('invisible');
37578         }
37579         
37580         this.originalValue = this.getValue();
37581         
37582     },
37583     
37584     inputEl: function ()
37585     {
37586         return this.el.select('.roo-radio-set-input', true).first();
37587     },
37588     
37589     getChildContainer : function()
37590     {
37591         return this.itemsEl;
37592     },
37593     
37594     register : function(item)
37595     {
37596         this.radioes.push(item);
37597         
37598     },
37599     
37600     validate : function()
37601     {   
37602         if(this.getVisibilityEl().hasClass('hidden')){
37603             return true;
37604         }
37605         
37606         var valid = false;
37607         
37608         Roo.each(this.radioes, function(i){
37609             if(!i.checked){
37610                 return;
37611             }
37612             
37613             valid = true;
37614             return false;
37615         });
37616         
37617         if(this.allowBlank) {
37618             return true;
37619         }
37620         
37621         if(this.disabled || valid){
37622             this.markValid();
37623             return true;
37624         }
37625         
37626         this.markInvalid();
37627         return false;
37628         
37629     },
37630     
37631     markValid : function()
37632     {
37633         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37634             this.indicatorEl().removeClass('visible');
37635             this.indicatorEl().addClass('invisible');
37636         }
37637         
37638         
37639         if (Roo.bootstrap.version == 3) {
37640             this.el.removeClass([this.invalidClass, this.validClass]);
37641             this.el.addClass(this.validClass);
37642         } else {
37643             this.el.removeClass(['is-invalid','is-valid']);
37644             this.el.addClass(['is-valid']);
37645         }
37646         this.fireEvent('valid', this);
37647     },
37648     
37649     markInvalid : function(msg)
37650     {
37651         if(this.allowBlank || this.disabled){
37652             return;
37653         }
37654         
37655         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37656             this.indicatorEl().removeClass('invisible');
37657             this.indicatorEl().addClass('visible');
37658         }
37659         if (Roo.bootstrap.version == 3) {
37660             this.el.removeClass([this.invalidClass, this.validClass]);
37661             this.el.addClass(this.invalidClass);
37662         } else {
37663             this.el.removeClass(['is-invalid','is-valid']);
37664             this.el.addClass(['is-invalid']);
37665         }
37666         
37667         this.fireEvent('invalid', this, msg);
37668         
37669     },
37670     
37671     setValue : function(v, suppressEvent)
37672     {   
37673         if(this.value === v){
37674             return;
37675         }
37676         
37677         this.value = v;
37678         
37679         if(this.rendered){
37680             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37681         }
37682         
37683         Roo.each(this.radioes, function(i){
37684             i.checked = false;
37685             i.el.removeClass('checked');
37686         });
37687         
37688         Roo.each(this.radioes, function(i){
37689             
37690             if(i.value === v || i.value.toString() === v.toString()){
37691                 i.checked = true;
37692                 i.el.addClass('checked');
37693                 
37694                 if(suppressEvent !== true){
37695                     this.fireEvent('check', this, i);
37696                 }
37697                 
37698                 return false;
37699             }
37700             
37701         }, this);
37702         
37703         this.validate();
37704     },
37705     
37706     clearInvalid : function(){
37707         
37708         if(!this.el || this.preventMark){
37709             return;
37710         }
37711         
37712         this.el.removeClass([this.invalidClass]);
37713         
37714         this.fireEvent('valid', this);
37715     }
37716     
37717 });
37718
37719 Roo.apply(Roo.bootstrap.RadioSet, {
37720     
37721     groups: {},
37722     
37723     register : function(set)
37724     {
37725         this.groups[set.name] = set;
37726     },
37727     
37728     get: function(name) 
37729     {
37730         if (typeof(this.groups[name]) == 'undefined') {
37731             return false;
37732         }
37733         
37734         return this.groups[name] ;
37735     }
37736     
37737 });
37738 /*
37739  * Based on:
37740  * Ext JS Library 1.1.1
37741  * Copyright(c) 2006-2007, Ext JS, LLC.
37742  *
37743  * Originally Released Under LGPL - original licence link has changed is not relivant.
37744  *
37745  * Fork - LGPL
37746  * <script type="text/javascript">
37747  */
37748
37749
37750 /**
37751  * @class Roo.bootstrap.SplitBar
37752  * @extends Roo.util.Observable
37753  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37754  * <br><br>
37755  * Usage:
37756  * <pre><code>
37757 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37758                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37759 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37760 split.minSize = 100;
37761 split.maxSize = 600;
37762 split.animate = true;
37763 split.on('moved', splitterMoved);
37764 </code></pre>
37765  * @constructor
37766  * Create a new SplitBar
37767  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37768  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37769  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37770  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37771                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37772                         position of the SplitBar).
37773  */
37774 Roo.bootstrap.SplitBar = function(cfg){
37775     
37776     /** @private */
37777     
37778     //{
37779     //  dragElement : elm
37780     //  resizingElement: el,
37781         // optional..
37782     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37783     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37784         // existingProxy ???
37785     //}
37786     
37787     this.el = Roo.get(cfg.dragElement, true);
37788     this.el.dom.unselectable = "on";
37789     /** @private */
37790     this.resizingEl = Roo.get(cfg.resizingElement, true);
37791
37792     /**
37793      * @private
37794      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37795      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37796      * @type Number
37797      */
37798     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37799     
37800     /**
37801      * The minimum size of the resizing element. (Defaults to 0)
37802      * @type Number
37803      */
37804     this.minSize = 0;
37805     
37806     /**
37807      * The maximum size of the resizing element. (Defaults to 2000)
37808      * @type Number
37809      */
37810     this.maxSize = 2000;
37811     
37812     /**
37813      * Whether to animate the transition to the new size
37814      * @type Boolean
37815      */
37816     this.animate = false;
37817     
37818     /**
37819      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37820      * @type Boolean
37821      */
37822     this.useShim = false;
37823     
37824     /** @private */
37825     this.shim = null;
37826     
37827     if(!cfg.existingProxy){
37828         /** @private */
37829         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37830     }else{
37831         this.proxy = Roo.get(cfg.existingProxy).dom;
37832     }
37833     /** @private */
37834     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37835     
37836     /** @private */
37837     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37838     
37839     /** @private */
37840     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37841     
37842     /** @private */
37843     this.dragSpecs = {};
37844     
37845     /**
37846      * @private The adapter to use to positon and resize elements
37847      */
37848     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37849     this.adapter.init(this);
37850     
37851     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37852         /** @private */
37853         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37854         this.el.addClass("roo-splitbar-h");
37855     }else{
37856         /** @private */
37857         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37858         this.el.addClass("roo-splitbar-v");
37859     }
37860     
37861     this.addEvents({
37862         /**
37863          * @event resize
37864          * Fires when the splitter is moved (alias for {@link #event-moved})
37865          * @param {Roo.bootstrap.SplitBar} this
37866          * @param {Number} newSize the new width or height
37867          */
37868         "resize" : true,
37869         /**
37870          * @event moved
37871          * Fires when the splitter is moved
37872          * @param {Roo.bootstrap.SplitBar} this
37873          * @param {Number} newSize the new width or height
37874          */
37875         "moved" : true,
37876         /**
37877          * @event beforeresize
37878          * Fires before the splitter is dragged
37879          * @param {Roo.bootstrap.SplitBar} this
37880          */
37881         "beforeresize" : true,
37882
37883         "beforeapply" : true
37884     });
37885
37886     Roo.util.Observable.call(this);
37887 };
37888
37889 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37890     onStartProxyDrag : function(x, y){
37891         this.fireEvent("beforeresize", this);
37892         if(!this.overlay){
37893             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37894             o.unselectable();
37895             o.enableDisplayMode("block");
37896             // all splitbars share the same overlay
37897             Roo.bootstrap.SplitBar.prototype.overlay = o;
37898         }
37899         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37900         this.overlay.show();
37901         Roo.get(this.proxy).setDisplayed("block");
37902         var size = this.adapter.getElementSize(this);
37903         this.activeMinSize = this.getMinimumSize();;
37904         this.activeMaxSize = this.getMaximumSize();;
37905         var c1 = size - this.activeMinSize;
37906         var c2 = Math.max(this.activeMaxSize - size, 0);
37907         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37908             this.dd.resetConstraints();
37909             this.dd.setXConstraint(
37910                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37911                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37912             );
37913             this.dd.setYConstraint(0, 0);
37914         }else{
37915             this.dd.resetConstraints();
37916             this.dd.setXConstraint(0, 0);
37917             this.dd.setYConstraint(
37918                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37919                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37920             );
37921          }
37922         this.dragSpecs.startSize = size;
37923         this.dragSpecs.startPoint = [x, y];
37924         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37925     },
37926     
37927     /** 
37928      * @private Called after the drag operation by the DDProxy
37929      */
37930     onEndProxyDrag : function(e){
37931         Roo.get(this.proxy).setDisplayed(false);
37932         var endPoint = Roo.lib.Event.getXY(e);
37933         if(this.overlay){
37934             this.overlay.hide();
37935         }
37936         var newSize;
37937         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37938             newSize = this.dragSpecs.startSize + 
37939                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37940                     endPoint[0] - this.dragSpecs.startPoint[0] :
37941                     this.dragSpecs.startPoint[0] - endPoint[0]
37942                 );
37943         }else{
37944             newSize = this.dragSpecs.startSize + 
37945                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37946                     endPoint[1] - this.dragSpecs.startPoint[1] :
37947                     this.dragSpecs.startPoint[1] - endPoint[1]
37948                 );
37949         }
37950         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37951         if(newSize != this.dragSpecs.startSize){
37952             if(this.fireEvent('beforeapply', this, newSize) !== false){
37953                 this.adapter.setElementSize(this, newSize);
37954                 this.fireEvent("moved", this, newSize);
37955                 this.fireEvent("resize", this, newSize);
37956             }
37957         }
37958     },
37959     
37960     /**
37961      * Get the adapter this SplitBar uses
37962      * @return The adapter object
37963      */
37964     getAdapter : function(){
37965         return this.adapter;
37966     },
37967     
37968     /**
37969      * Set the adapter this SplitBar uses
37970      * @param {Object} adapter A SplitBar adapter object
37971      */
37972     setAdapter : function(adapter){
37973         this.adapter = adapter;
37974         this.adapter.init(this);
37975     },
37976     
37977     /**
37978      * Gets the minimum size for the resizing element
37979      * @return {Number} The minimum size
37980      */
37981     getMinimumSize : function(){
37982         return this.minSize;
37983     },
37984     
37985     /**
37986      * Sets the minimum size for the resizing element
37987      * @param {Number} minSize The minimum size
37988      */
37989     setMinimumSize : function(minSize){
37990         this.minSize = minSize;
37991     },
37992     
37993     /**
37994      * Gets the maximum size for the resizing element
37995      * @return {Number} The maximum size
37996      */
37997     getMaximumSize : function(){
37998         return this.maxSize;
37999     },
38000     
38001     /**
38002      * Sets the maximum size for the resizing element
38003      * @param {Number} maxSize The maximum size
38004      */
38005     setMaximumSize : function(maxSize){
38006         this.maxSize = maxSize;
38007     },
38008     
38009     /**
38010      * Sets the initialize size for the resizing element
38011      * @param {Number} size The initial size
38012      */
38013     setCurrentSize : function(size){
38014         var oldAnimate = this.animate;
38015         this.animate = false;
38016         this.adapter.setElementSize(this, size);
38017         this.animate = oldAnimate;
38018     },
38019     
38020     /**
38021      * Destroy this splitbar. 
38022      * @param {Boolean} removeEl True to remove the element
38023      */
38024     destroy : function(removeEl){
38025         if(this.shim){
38026             this.shim.remove();
38027         }
38028         this.dd.unreg();
38029         this.proxy.parentNode.removeChild(this.proxy);
38030         if(removeEl){
38031             this.el.remove();
38032         }
38033     }
38034 });
38035
38036 /**
38037  * @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.
38038  */
38039 Roo.bootstrap.SplitBar.createProxy = function(dir){
38040     var proxy = new Roo.Element(document.createElement("div"));
38041     proxy.unselectable();
38042     var cls = 'roo-splitbar-proxy';
38043     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38044     document.body.appendChild(proxy.dom);
38045     return proxy.dom;
38046 };
38047
38048 /** 
38049  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38050  * Default Adapter. It assumes the splitter and resizing element are not positioned
38051  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38052  */
38053 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38054 };
38055
38056 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38057     // do nothing for now
38058     init : function(s){
38059     
38060     },
38061     /**
38062      * Called before drag operations to get the current size of the resizing element. 
38063      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38064      */
38065      getElementSize : function(s){
38066         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38067             return s.resizingEl.getWidth();
38068         }else{
38069             return s.resizingEl.getHeight();
38070         }
38071     },
38072     
38073     /**
38074      * Called after drag operations to set the size of the resizing element.
38075      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38076      * @param {Number} newSize The new size to set
38077      * @param {Function} onComplete A function to be invoked when resizing is complete
38078      */
38079     setElementSize : function(s, newSize, onComplete){
38080         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38081             if(!s.animate){
38082                 s.resizingEl.setWidth(newSize);
38083                 if(onComplete){
38084                     onComplete(s, newSize);
38085                 }
38086             }else{
38087                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38088             }
38089         }else{
38090             
38091             if(!s.animate){
38092                 s.resizingEl.setHeight(newSize);
38093                 if(onComplete){
38094                     onComplete(s, newSize);
38095                 }
38096             }else{
38097                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38098             }
38099         }
38100     }
38101 };
38102
38103 /** 
38104  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38105  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38106  * Adapter that  moves the splitter element to align with the resized sizing element. 
38107  * Used with an absolute positioned SplitBar.
38108  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38109  * document.body, make sure you assign an id to the body element.
38110  */
38111 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38112     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38113     this.container = Roo.get(container);
38114 };
38115
38116 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38117     init : function(s){
38118         this.basic.init(s);
38119     },
38120     
38121     getElementSize : function(s){
38122         return this.basic.getElementSize(s);
38123     },
38124     
38125     setElementSize : function(s, newSize, onComplete){
38126         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38127     },
38128     
38129     moveSplitter : function(s){
38130         var yes = Roo.bootstrap.SplitBar;
38131         switch(s.placement){
38132             case yes.LEFT:
38133                 s.el.setX(s.resizingEl.getRight());
38134                 break;
38135             case yes.RIGHT:
38136                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38137                 break;
38138             case yes.TOP:
38139                 s.el.setY(s.resizingEl.getBottom());
38140                 break;
38141             case yes.BOTTOM:
38142                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38143                 break;
38144         }
38145     }
38146 };
38147
38148 /**
38149  * Orientation constant - Create a vertical SplitBar
38150  * @static
38151  * @type Number
38152  */
38153 Roo.bootstrap.SplitBar.VERTICAL = 1;
38154
38155 /**
38156  * Orientation constant - Create a horizontal SplitBar
38157  * @static
38158  * @type Number
38159  */
38160 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38161
38162 /**
38163  * Placement constant - The resizing element is to the left of the splitter element
38164  * @static
38165  * @type Number
38166  */
38167 Roo.bootstrap.SplitBar.LEFT = 1;
38168
38169 /**
38170  * Placement constant - The resizing element is to the right of the splitter element
38171  * @static
38172  * @type Number
38173  */
38174 Roo.bootstrap.SplitBar.RIGHT = 2;
38175
38176 /**
38177  * Placement constant - The resizing element is positioned above the splitter element
38178  * @static
38179  * @type Number
38180  */
38181 Roo.bootstrap.SplitBar.TOP = 3;
38182
38183 /**
38184  * Placement constant - The resizing element is positioned under splitter element
38185  * @static
38186  * @type Number
38187  */
38188 Roo.bootstrap.SplitBar.BOTTOM = 4;
38189 Roo.namespace("Roo.bootstrap.layout");/*
38190  * Based on:
38191  * Ext JS Library 1.1.1
38192  * Copyright(c) 2006-2007, Ext JS, LLC.
38193  *
38194  * Originally Released Under LGPL - original licence link has changed is not relivant.
38195  *
38196  * Fork - LGPL
38197  * <script type="text/javascript">
38198  */
38199
38200 /**
38201  * @class Roo.bootstrap.layout.Manager
38202  * @extends Roo.bootstrap.Component
38203  * Base class for layout managers.
38204  */
38205 Roo.bootstrap.layout.Manager = function(config)
38206 {
38207     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38208
38209
38210
38211
38212
38213     /** false to disable window resize monitoring @type Boolean */
38214     this.monitorWindowResize = true;
38215     this.regions = {};
38216     this.addEvents({
38217         /**
38218          * @event layout
38219          * Fires when a layout is performed.
38220          * @param {Roo.LayoutManager} this
38221          */
38222         "layout" : true,
38223         /**
38224          * @event regionresized
38225          * Fires when the user resizes a region.
38226          * @param {Roo.LayoutRegion} region The resized region
38227          * @param {Number} newSize The new size (width for east/west, height for north/south)
38228          */
38229         "regionresized" : true,
38230         /**
38231          * @event regioncollapsed
38232          * Fires when a region is collapsed.
38233          * @param {Roo.LayoutRegion} region The collapsed region
38234          */
38235         "regioncollapsed" : true,
38236         /**
38237          * @event regionexpanded
38238          * Fires when a region is expanded.
38239          * @param {Roo.LayoutRegion} region The expanded region
38240          */
38241         "regionexpanded" : true
38242     });
38243     this.updating = false;
38244
38245     if (config.el) {
38246         this.el = Roo.get(config.el);
38247         this.initEvents();
38248     }
38249
38250 };
38251
38252 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38253
38254
38255     regions : null,
38256
38257     monitorWindowResize : true,
38258
38259
38260     updating : false,
38261
38262
38263     onRender : function(ct, position)
38264     {
38265         if(!this.el){
38266             this.el = Roo.get(ct);
38267             this.initEvents();
38268         }
38269         //this.fireEvent('render',this);
38270     },
38271
38272
38273     initEvents: function()
38274     {
38275
38276
38277         // ie scrollbar fix
38278         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38279             document.body.scroll = "no";
38280         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38281             this.el.position('relative');
38282         }
38283         this.id = this.el.id;
38284         this.el.addClass("roo-layout-container");
38285         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38286         if(this.el.dom != document.body ) {
38287             this.el.on('resize', this.layout,this);
38288             this.el.on('show', this.layout,this);
38289         }
38290
38291     },
38292
38293     /**
38294      * Returns true if this layout is currently being updated
38295      * @return {Boolean}
38296      */
38297     isUpdating : function(){
38298         return this.updating;
38299     },
38300
38301     /**
38302      * Suspend the LayoutManager from doing auto-layouts while
38303      * making multiple add or remove calls
38304      */
38305     beginUpdate : function(){
38306         this.updating = true;
38307     },
38308
38309     /**
38310      * Restore auto-layouts and optionally disable the manager from performing a layout
38311      * @param {Boolean} noLayout true to disable a layout update
38312      */
38313     endUpdate : function(noLayout){
38314         this.updating = false;
38315         if(!noLayout){
38316             this.layout();
38317         }
38318     },
38319
38320     layout: function(){
38321         // abstract...
38322     },
38323
38324     onRegionResized : function(region, newSize){
38325         this.fireEvent("regionresized", region, newSize);
38326         this.layout();
38327     },
38328
38329     onRegionCollapsed : function(region){
38330         this.fireEvent("regioncollapsed", region);
38331     },
38332
38333     onRegionExpanded : function(region){
38334         this.fireEvent("regionexpanded", region);
38335     },
38336
38337     /**
38338      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38339      * performs box-model adjustments.
38340      * @return {Object} The size as an object {width: (the width), height: (the height)}
38341      */
38342     getViewSize : function()
38343     {
38344         var size;
38345         if(this.el.dom != document.body){
38346             size = this.el.getSize();
38347         }else{
38348             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38349         }
38350         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38351         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38352         return size;
38353     },
38354
38355     /**
38356      * Returns the Element this layout is bound to.
38357      * @return {Roo.Element}
38358      */
38359     getEl : function(){
38360         return this.el;
38361     },
38362
38363     /**
38364      * Returns the specified region.
38365      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38366      * @return {Roo.LayoutRegion}
38367      */
38368     getRegion : function(target){
38369         return this.regions[target.toLowerCase()];
38370     },
38371
38372     onWindowResize : function(){
38373         if(this.monitorWindowResize){
38374             this.layout();
38375         }
38376     }
38377 });
38378 /*
38379  * Based on:
38380  * Ext JS Library 1.1.1
38381  * Copyright(c) 2006-2007, Ext JS, LLC.
38382  *
38383  * Originally Released Under LGPL - original licence link has changed is not relivant.
38384  *
38385  * Fork - LGPL
38386  * <script type="text/javascript">
38387  */
38388 /**
38389  * @class Roo.bootstrap.layout.Border
38390  * @extends Roo.bootstrap.layout.Manager
38391  * @builder-top
38392  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38393  * please see: examples/bootstrap/nested.html<br><br>
38394  
38395 <b>The container the layout is rendered into can be either the body element or any other element.
38396 If it is not the body element, the container needs to either be an absolute positioned element,
38397 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38398 the container size if it is not the body element.</b>
38399
38400 * @constructor
38401 * Create a new Border
38402 * @param {Object} config Configuration options
38403  */
38404 Roo.bootstrap.layout.Border = function(config){
38405     config = config || {};
38406     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38407     
38408     
38409     
38410     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38411         if(config[region]){
38412             config[region].region = region;
38413             this.addRegion(config[region]);
38414         }
38415     },this);
38416     
38417 };
38418
38419 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38420
38421 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38422     
38423     parent : false, // this might point to a 'nest' or a ???
38424     
38425     /**
38426      * Creates and adds a new region if it doesn't already exist.
38427      * @param {String} target The target region key (north, south, east, west or center).
38428      * @param {Object} config The regions config object
38429      * @return {BorderLayoutRegion} The new region
38430      */
38431     addRegion : function(config)
38432     {
38433         if(!this.regions[config.region]){
38434             var r = this.factory(config);
38435             this.bindRegion(r);
38436         }
38437         return this.regions[config.region];
38438     },
38439
38440     // private (kinda)
38441     bindRegion : function(r){
38442         this.regions[r.config.region] = r;
38443         
38444         r.on("visibilitychange",    this.layout, this);
38445         r.on("paneladded",          this.layout, this);
38446         r.on("panelremoved",        this.layout, this);
38447         r.on("invalidated",         this.layout, this);
38448         r.on("resized",             this.onRegionResized, this);
38449         r.on("collapsed",           this.onRegionCollapsed, this);
38450         r.on("expanded",            this.onRegionExpanded, this);
38451     },
38452
38453     /**
38454      * Performs a layout update.
38455      */
38456     layout : function()
38457     {
38458         if(this.updating) {
38459             return;
38460         }
38461         
38462         // render all the rebions if they have not been done alreayd?
38463         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38464             if(this.regions[region] && !this.regions[region].bodyEl){
38465                 this.regions[region].onRender(this.el)
38466             }
38467         },this);
38468         
38469         var size = this.getViewSize();
38470         var w = size.width;
38471         var h = size.height;
38472         var centerW = w;
38473         var centerH = h;
38474         var centerY = 0;
38475         var centerX = 0;
38476         //var x = 0, y = 0;
38477
38478         var rs = this.regions;
38479         var north = rs["north"];
38480         var south = rs["south"]; 
38481         var west = rs["west"];
38482         var east = rs["east"];
38483         var center = rs["center"];
38484         //if(this.hideOnLayout){ // not supported anymore
38485             //c.el.setStyle("display", "none");
38486         //}
38487         if(north && north.isVisible()){
38488             var b = north.getBox();
38489             var m = north.getMargins();
38490             b.width = w - (m.left+m.right);
38491             b.x = m.left;
38492             b.y = m.top;
38493             centerY = b.height + b.y + m.bottom;
38494             centerH -= centerY;
38495             north.updateBox(this.safeBox(b));
38496         }
38497         if(south && south.isVisible()){
38498             var b = south.getBox();
38499             var m = south.getMargins();
38500             b.width = w - (m.left+m.right);
38501             b.x = m.left;
38502             var totalHeight = (b.height + m.top + m.bottom);
38503             b.y = h - totalHeight + m.top;
38504             centerH -= totalHeight;
38505             south.updateBox(this.safeBox(b));
38506         }
38507         if(west && west.isVisible()){
38508             var b = west.getBox();
38509             var m = west.getMargins();
38510             b.height = centerH - (m.top+m.bottom);
38511             b.x = m.left;
38512             b.y = centerY + m.top;
38513             var totalWidth = (b.width + m.left + m.right);
38514             centerX += totalWidth;
38515             centerW -= totalWidth;
38516             west.updateBox(this.safeBox(b));
38517         }
38518         if(east && east.isVisible()){
38519             var b = east.getBox();
38520             var m = east.getMargins();
38521             b.height = centerH - (m.top+m.bottom);
38522             var totalWidth = (b.width + m.left + m.right);
38523             b.x = w - totalWidth + m.left;
38524             b.y = centerY + m.top;
38525             centerW -= totalWidth;
38526             east.updateBox(this.safeBox(b));
38527         }
38528         if(center){
38529             var m = center.getMargins();
38530             var centerBox = {
38531                 x: centerX + m.left,
38532                 y: centerY + m.top,
38533                 width: centerW - (m.left+m.right),
38534                 height: centerH - (m.top+m.bottom)
38535             };
38536             //if(this.hideOnLayout){
38537                 //center.el.setStyle("display", "block");
38538             //}
38539             center.updateBox(this.safeBox(centerBox));
38540         }
38541         this.el.repaint();
38542         this.fireEvent("layout", this);
38543     },
38544
38545     // private
38546     safeBox : function(box){
38547         box.width = Math.max(0, box.width);
38548         box.height = Math.max(0, box.height);
38549         return box;
38550     },
38551
38552     /**
38553      * Adds a ContentPanel (or subclass) to this layout.
38554      * @param {String} target The target region key (north, south, east, west or center).
38555      * @param {Roo.ContentPanel} panel The panel to add
38556      * @return {Roo.ContentPanel} The added panel
38557      */
38558     add : function(target, panel){
38559          
38560         target = target.toLowerCase();
38561         return this.regions[target].add(panel);
38562     },
38563
38564     /**
38565      * Remove a ContentPanel (or subclass) to this layout.
38566      * @param {String} target The target region key (north, south, east, west or center).
38567      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38568      * @return {Roo.ContentPanel} The removed panel
38569      */
38570     remove : function(target, panel){
38571         target = target.toLowerCase();
38572         return this.regions[target].remove(panel);
38573     },
38574
38575     /**
38576      * Searches all regions for a panel with the specified id
38577      * @param {String} panelId
38578      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38579      */
38580     findPanel : function(panelId){
38581         var rs = this.regions;
38582         for(var target in rs){
38583             if(typeof rs[target] != "function"){
38584                 var p = rs[target].getPanel(panelId);
38585                 if(p){
38586                     return p;
38587                 }
38588             }
38589         }
38590         return null;
38591     },
38592
38593     /**
38594      * Searches all regions for a panel with the specified id and activates (shows) it.
38595      * @param {String/ContentPanel} panelId The panels id or the panel itself
38596      * @return {Roo.ContentPanel} The shown panel or null
38597      */
38598     showPanel : function(panelId) {
38599       var rs = this.regions;
38600       for(var target in rs){
38601          var r = rs[target];
38602          if(typeof r != "function"){
38603             if(r.hasPanel(panelId)){
38604                return r.showPanel(panelId);
38605             }
38606          }
38607       }
38608       return null;
38609    },
38610
38611    /**
38612      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38613      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38614      */
38615    /*
38616     restoreState : function(provider){
38617         if(!provider){
38618             provider = Roo.state.Manager;
38619         }
38620         var sm = new Roo.LayoutStateManager();
38621         sm.init(this, provider);
38622     },
38623 */
38624  
38625  
38626     /**
38627      * Adds a xtype elements to the layout.
38628      * <pre><code>
38629
38630 layout.addxtype({
38631        xtype : 'ContentPanel',
38632        region: 'west',
38633        items: [ .... ]
38634    }
38635 );
38636
38637 layout.addxtype({
38638         xtype : 'NestedLayoutPanel',
38639         region: 'west',
38640         layout: {
38641            center: { },
38642            west: { }   
38643         },
38644         items : [ ... list of content panels or nested layout panels.. ]
38645    }
38646 );
38647 </code></pre>
38648      * @param {Object} cfg Xtype definition of item to add.
38649      */
38650     addxtype : function(cfg)
38651     {
38652         // basically accepts a pannel...
38653         // can accept a layout region..!?!?
38654         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38655         
38656         
38657         // theory?  children can only be panels??
38658         
38659         //if (!cfg.xtype.match(/Panel$/)) {
38660         //    return false;
38661         //}
38662         var ret = false;
38663         
38664         if (typeof(cfg.region) == 'undefined') {
38665             Roo.log("Failed to add Panel, region was not set");
38666             Roo.log(cfg);
38667             return false;
38668         }
38669         var region = cfg.region;
38670         delete cfg.region;
38671         
38672           
38673         var xitems = [];
38674         if (cfg.items) {
38675             xitems = cfg.items;
38676             delete cfg.items;
38677         }
38678         var nb = false;
38679         
38680         if ( region == 'center') {
38681             Roo.log("Center: " + cfg.title);
38682         }
38683         
38684         
38685         switch(cfg.xtype) 
38686         {
38687             case 'Content':  // ContentPanel (el, cfg)
38688             case 'Scroll':  // ContentPanel (el, cfg)
38689             case 'View': 
38690                 cfg.autoCreate = cfg.autoCreate || true;
38691                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38692                 //} else {
38693                 //    var el = this.el.createChild();
38694                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38695                 //}
38696                 
38697                 this.add(region, ret);
38698                 break;
38699             
38700             /*
38701             case 'TreePanel': // our new panel!
38702                 cfg.el = this.el.createChild();
38703                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38704                 this.add(region, ret);
38705                 break;
38706             */
38707             
38708             case 'Nest': 
38709                 // create a new Layout (which is  a Border Layout...
38710                 
38711                 var clayout = cfg.layout;
38712                 clayout.el  = this.el.createChild();
38713                 clayout.items   = clayout.items  || [];
38714                 
38715                 delete cfg.layout;
38716                 
38717                 // replace this exitems with the clayout ones..
38718                 xitems = clayout.items;
38719                  
38720                 // force background off if it's in center...
38721                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38722                     cfg.background = false;
38723                 }
38724                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38725                 
38726                 
38727                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38728                 //console.log('adding nested layout panel '  + cfg.toSource());
38729                 this.add(region, ret);
38730                 nb = {}; /// find first...
38731                 break;
38732             
38733             case 'Grid':
38734                 
38735                 // needs grid and region
38736                 
38737                 //var el = this.getRegion(region).el.createChild();
38738                 /*
38739                  *var el = this.el.createChild();
38740                 // create the grid first...
38741                 cfg.grid.container = el;
38742                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38743                 */
38744                 
38745                 if (region == 'center' && this.active ) {
38746                     cfg.background = false;
38747                 }
38748                 
38749                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38750                 
38751                 this.add(region, ret);
38752                 /*
38753                 if (cfg.background) {
38754                     // render grid on panel activation (if panel background)
38755                     ret.on('activate', function(gp) {
38756                         if (!gp.grid.rendered) {
38757                     //        gp.grid.render(el);
38758                         }
38759                     });
38760                 } else {
38761                   //  cfg.grid.render(el);
38762                 }
38763                 */
38764                 break;
38765            
38766            
38767             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38768                 // it was the old xcomponent building that caused this before.
38769                 // espeically if border is the top element in the tree.
38770                 ret = this;
38771                 break; 
38772                 
38773                     
38774                 
38775                 
38776                 
38777             default:
38778                 /*
38779                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38780                     
38781                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38782                     this.add(region, ret);
38783                 } else {
38784                 */
38785                     Roo.log(cfg);
38786                     throw "Can not add '" + cfg.xtype + "' to Border";
38787                     return null;
38788              
38789                                 
38790              
38791         }
38792         this.beginUpdate();
38793         // add children..
38794         var region = '';
38795         var abn = {};
38796         Roo.each(xitems, function(i)  {
38797             region = nb && i.region ? i.region : false;
38798             
38799             var add = ret.addxtype(i);
38800            
38801             if (region) {
38802                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38803                 if (!i.background) {
38804                     abn[region] = nb[region] ;
38805                 }
38806             }
38807             
38808         });
38809         this.endUpdate();
38810
38811         // make the last non-background panel active..
38812         //if (nb) { Roo.log(abn); }
38813         if (nb) {
38814             
38815             for(var r in abn) {
38816                 region = this.getRegion(r);
38817                 if (region) {
38818                     // tried using nb[r], but it does not work..
38819                      
38820                     region.showPanel(abn[r]);
38821                    
38822                 }
38823             }
38824         }
38825         return ret;
38826         
38827     },
38828     
38829     
38830 // private
38831     factory : function(cfg)
38832     {
38833         
38834         var validRegions = Roo.bootstrap.layout.Border.regions;
38835
38836         var target = cfg.region;
38837         cfg.mgr = this;
38838         
38839         var r = Roo.bootstrap.layout;
38840         Roo.log(target);
38841         switch(target){
38842             case "north":
38843                 return new r.North(cfg);
38844             case "south":
38845                 return new r.South(cfg);
38846             case "east":
38847                 return new r.East(cfg);
38848             case "west":
38849                 return new r.West(cfg);
38850             case "center":
38851                 return new r.Center(cfg);
38852         }
38853         throw 'Layout region "'+target+'" not supported.';
38854     }
38855     
38856     
38857 });
38858  /*
38859  * Based on:
38860  * Ext JS Library 1.1.1
38861  * Copyright(c) 2006-2007, Ext JS, LLC.
38862  *
38863  * Originally Released Under LGPL - original licence link has changed is not relivant.
38864  *
38865  * Fork - LGPL
38866  * <script type="text/javascript">
38867  */
38868  
38869 /**
38870  * @class Roo.bootstrap.layout.Basic
38871  * @extends Roo.util.Observable
38872  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38873  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38874  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38875  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38876  * @cfg {string}   region  the region that it inhabits..
38877  * @cfg {bool}   skipConfig skip config?
38878  * 
38879
38880  */
38881 Roo.bootstrap.layout.Basic = function(config){
38882     
38883     this.mgr = config.mgr;
38884     
38885     this.position = config.region;
38886     
38887     var skipConfig = config.skipConfig;
38888     
38889     this.events = {
38890         /**
38891          * @scope Roo.BasicLayoutRegion
38892          */
38893         
38894         /**
38895          * @event beforeremove
38896          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38897          * @param {Roo.LayoutRegion} this
38898          * @param {Roo.ContentPanel} panel The panel
38899          * @param {Object} e The cancel event object
38900          */
38901         "beforeremove" : true,
38902         /**
38903          * @event invalidated
38904          * Fires when the layout for this region is changed.
38905          * @param {Roo.LayoutRegion} this
38906          */
38907         "invalidated" : true,
38908         /**
38909          * @event visibilitychange
38910          * Fires when this region is shown or hidden 
38911          * @param {Roo.LayoutRegion} this
38912          * @param {Boolean} visibility true or false
38913          */
38914         "visibilitychange" : true,
38915         /**
38916          * @event paneladded
38917          * Fires when a panel is added. 
38918          * @param {Roo.LayoutRegion} this
38919          * @param {Roo.ContentPanel} panel The panel
38920          */
38921         "paneladded" : true,
38922         /**
38923          * @event panelremoved
38924          * Fires when a panel is removed. 
38925          * @param {Roo.LayoutRegion} this
38926          * @param {Roo.ContentPanel} panel The panel
38927          */
38928         "panelremoved" : true,
38929         /**
38930          * @event beforecollapse
38931          * Fires when this region before collapse.
38932          * @param {Roo.LayoutRegion} this
38933          */
38934         "beforecollapse" : true,
38935         /**
38936          * @event collapsed
38937          * Fires when this region is collapsed.
38938          * @param {Roo.LayoutRegion} this
38939          */
38940         "collapsed" : true,
38941         /**
38942          * @event expanded
38943          * Fires when this region is expanded.
38944          * @param {Roo.LayoutRegion} this
38945          */
38946         "expanded" : true,
38947         /**
38948          * @event slideshow
38949          * Fires when this region is slid into view.
38950          * @param {Roo.LayoutRegion} this
38951          */
38952         "slideshow" : true,
38953         /**
38954          * @event slidehide
38955          * Fires when this region slides out of view. 
38956          * @param {Roo.LayoutRegion} this
38957          */
38958         "slidehide" : true,
38959         /**
38960          * @event panelactivated
38961          * Fires when a panel is activated. 
38962          * @param {Roo.LayoutRegion} this
38963          * @param {Roo.ContentPanel} panel The activated panel
38964          */
38965         "panelactivated" : true,
38966         /**
38967          * @event resized
38968          * Fires when the user resizes this region. 
38969          * @param {Roo.LayoutRegion} this
38970          * @param {Number} newSize The new size (width for east/west, height for north/south)
38971          */
38972         "resized" : true
38973     };
38974     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38975     this.panels = new Roo.util.MixedCollection();
38976     this.panels.getKey = this.getPanelId.createDelegate(this);
38977     this.box = null;
38978     this.activePanel = null;
38979     // ensure listeners are added...
38980     
38981     if (config.listeners || config.events) {
38982         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38983             listeners : config.listeners || {},
38984             events : config.events || {}
38985         });
38986     }
38987     
38988     if(skipConfig !== true){
38989         this.applyConfig(config);
38990     }
38991 };
38992
38993 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38994 {
38995     getPanelId : function(p){
38996         return p.getId();
38997     },
38998     
38999     applyConfig : function(config){
39000         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39001         this.config = config;
39002         
39003     },
39004     
39005     /**
39006      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
39007      * the width, for horizontal (north, south) the height.
39008      * @param {Number} newSize The new width or height
39009      */
39010     resizeTo : function(newSize){
39011         var el = this.el ? this.el :
39012                  (this.activePanel ? this.activePanel.getEl() : null);
39013         if(el){
39014             switch(this.position){
39015                 case "east":
39016                 case "west":
39017                     el.setWidth(newSize);
39018                     this.fireEvent("resized", this, newSize);
39019                 break;
39020                 case "north":
39021                 case "south":
39022                     el.setHeight(newSize);
39023                     this.fireEvent("resized", this, newSize);
39024                 break;                
39025             }
39026         }
39027     },
39028     
39029     getBox : function(){
39030         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39031     },
39032     
39033     getMargins : function(){
39034         return this.margins;
39035     },
39036     
39037     updateBox : function(box){
39038         this.box = box;
39039         var el = this.activePanel.getEl();
39040         el.dom.style.left = box.x + "px";
39041         el.dom.style.top = box.y + "px";
39042         this.activePanel.setSize(box.width, box.height);
39043     },
39044     
39045     /**
39046      * Returns the container element for this region.
39047      * @return {Roo.Element}
39048      */
39049     getEl : function(){
39050         return this.activePanel;
39051     },
39052     
39053     /**
39054      * Returns true if this region is currently visible.
39055      * @return {Boolean}
39056      */
39057     isVisible : function(){
39058         return this.activePanel ? true : false;
39059     },
39060     
39061     setActivePanel : function(panel){
39062         panel = this.getPanel(panel);
39063         if(this.activePanel && this.activePanel != panel){
39064             this.activePanel.setActiveState(false);
39065             this.activePanel.getEl().setLeftTop(-10000,-10000);
39066         }
39067         this.activePanel = panel;
39068         panel.setActiveState(true);
39069         if(this.box){
39070             panel.setSize(this.box.width, this.box.height);
39071         }
39072         this.fireEvent("panelactivated", this, panel);
39073         this.fireEvent("invalidated");
39074     },
39075     
39076     /**
39077      * Show the specified panel.
39078      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39079      * @return {Roo.ContentPanel} The shown panel or null
39080      */
39081     showPanel : function(panel){
39082         panel = this.getPanel(panel);
39083         if(panel){
39084             this.setActivePanel(panel);
39085         }
39086         return panel;
39087     },
39088     
39089     /**
39090      * Get the active panel for this region.
39091      * @return {Roo.ContentPanel} The active panel or null
39092      */
39093     getActivePanel : function(){
39094         return this.activePanel;
39095     },
39096     
39097     /**
39098      * Add the passed ContentPanel(s)
39099      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39100      * @return {Roo.ContentPanel} The panel added (if only one was added)
39101      */
39102     add : function(panel){
39103         if(arguments.length > 1){
39104             for(var i = 0, len = arguments.length; i < len; i++) {
39105                 this.add(arguments[i]);
39106             }
39107             return null;
39108         }
39109         if(this.hasPanel(panel)){
39110             this.showPanel(panel);
39111             return panel;
39112         }
39113         var el = panel.getEl();
39114         if(el.dom.parentNode != this.mgr.el.dom){
39115             this.mgr.el.dom.appendChild(el.dom);
39116         }
39117         if(panel.setRegion){
39118             panel.setRegion(this);
39119         }
39120         this.panels.add(panel);
39121         el.setStyle("position", "absolute");
39122         if(!panel.background){
39123             this.setActivePanel(panel);
39124             if(this.config.initialSize && this.panels.getCount()==1){
39125                 this.resizeTo(this.config.initialSize);
39126             }
39127         }
39128         this.fireEvent("paneladded", this, panel);
39129         return panel;
39130     },
39131     
39132     /**
39133      * Returns true if the panel is in this region.
39134      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39135      * @return {Boolean}
39136      */
39137     hasPanel : function(panel){
39138         if(typeof panel == "object"){ // must be panel obj
39139             panel = panel.getId();
39140         }
39141         return this.getPanel(panel) ? true : false;
39142     },
39143     
39144     /**
39145      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39146      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39147      * @param {Boolean} preservePanel Overrides the config preservePanel option
39148      * @return {Roo.ContentPanel} The panel that was removed
39149      */
39150     remove : function(panel, preservePanel){
39151         panel = this.getPanel(panel);
39152         if(!panel){
39153             return null;
39154         }
39155         var e = {};
39156         this.fireEvent("beforeremove", this, panel, e);
39157         if(e.cancel === true){
39158             return null;
39159         }
39160         var panelId = panel.getId();
39161         this.panels.removeKey(panelId);
39162         return panel;
39163     },
39164     
39165     /**
39166      * Returns the panel specified or null if it's not in this region.
39167      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39168      * @return {Roo.ContentPanel}
39169      */
39170     getPanel : function(id){
39171         if(typeof id == "object"){ // must be panel obj
39172             return id;
39173         }
39174         return this.panels.get(id);
39175     },
39176     
39177     /**
39178      * Returns this regions position (north/south/east/west/center).
39179      * @return {String} 
39180      */
39181     getPosition: function(){
39182         return this.position;    
39183     }
39184 });/*
39185  * Based on:
39186  * Ext JS Library 1.1.1
39187  * Copyright(c) 2006-2007, Ext JS, LLC.
39188  *
39189  * Originally Released Under LGPL - original licence link has changed is not relivant.
39190  *
39191  * Fork - LGPL
39192  * <script type="text/javascript">
39193  */
39194  
39195 /**
39196  * @class Roo.bootstrap.layout.Region
39197  * @extends Roo.bootstrap.layout.Basic
39198  * This class represents a region in a layout manager.
39199  
39200  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39201  * @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})
39202  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39203  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39204  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39205  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39206  * @cfg {String}    title           The title for the region (overrides panel titles)
39207  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39208  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39209  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39210  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39211  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39212  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39213  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39214  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39215  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39216  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39217
39218  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39219  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39220  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39221  * @cfg {Number}    width           For East/West panels
39222  * @cfg {Number}    height          For North/South panels
39223  * @cfg {Boolean}   split           To show the splitter
39224  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39225  * 
39226  * @cfg {string}   cls             Extra CSS classes to add to region
39227  * 
39228  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39229  * @cfg {string}   region  the region that it inhabits..
39230  *
39231
39232  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39233  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39234
39235  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39236  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39237  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39238  */
39239 Roo.bootstrap.layout.Region = function(config)
39240 {
39241     this.applyConfig(config);
39242
39243     var mgr = config.mgr;
39244     var pos = config.region;
39245     config.skipConfig = true;
39246     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39247     
39248     if (mgr.el) {
39249         this.onRender(mgr.el);   
39250     }
39251      
39252     this.visible = true;
39253     this.collapsed = false;
39254     this.unrendered_panels = [];
39255 };
39256
39257 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39258
39259     position: '', // set by wrapper (eg. north/south etc..)
39260     unrendered_panels : null,  // unrendered panels.
39261     
39262     tabPosition : false,
39263     
39264     mgr: false, // points to 'Border'
39265     
39266     
39267     createBody : function(){
39268         /** This region's body element 
39269         * @type Roo.Element */
39270         this.bodyEl = this.el.createChild({
39271                 tag: "div",
39272                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39273         });
39274     },
39275
39276     onRender: function(ctr, pos)
39277     {
39278         var dh = Roo.DomHelper;
39279         /** This region's container element 
39280         * @type Roo.Element */
39281         this.el = dh.append(ctr.dom, {
39282                 tag: "div",
39283                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39284             }, true);
39285         /** This region's title element 
39286         * @type Roo.Element */
39287     
39288         this.titleEl = dh.append(this.el.dom,  {
39289                 tag: "div",
39290                 unselectable: "on",
39291                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39292                 children:[
39293                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39294                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39295                 ]
39296             }, true);
39297         
39298         this.titleEl.enableDisplayMode();
39299         /** This region's title text element 
39300         * @type HTMLElement */
39301         this.titleTextEl = this.titleEl.dom.firstChild;
39302         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39303         /*
39304         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39305         this.closeBtn.enableDisplayMode();
39306         this.closeBtn.on("click", this.closeClicked, this);
39307         this.closeBtn.hide();
39308     */
39309         this.createBody(this.config);
39310         if(this.config.hideWhenEmpty){
39311             this.hide();
39312             this.on("paneladded", this.validateVisibility, this);
39313             this.on("panelremoved", this.validateVisibility, this);
39314         }
39315         if(this.autoScroll){
39316             this.bodyEl.setStyle("overflow", "auto");
39317         }else{
39318             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39319         }
39320         //if(c.titlebar !== false){
39321             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39322                 this.titleEl.hide();
39323             }else{
39324                 this.titleEl.show();
39325                 if(this.config.title){
39326                     this.titleTextEl.innerHTML = this.config.title;
39327                 }
39328             }
39329         //}
39330         if(this.config.collapsed){
39331             this.collapse(true);
39332         }
39333         if(this.config.hidden){
39334             this.hide();
39335         }
39336         
39337         if (this.unrendered_panels && this.unrendered_panels.length) {
39338             for (var i =0;i< this.unrendered_panels.length; i++) {
39339                 this.add(this.unrendered_panels[i]);
39340             }
39341             this.unrendered_panels = null;
39342             
39343         }
39344         
39345     },
39346     
39347     applyConfig : function(c)
39348     {
39349         /*
39350          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39351             var dh = Roo.DomHelper;
39352             if(c.titlebar !== false){
39353                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39354                 this.collapseBtn.on("click", this.collapse, this);
39355                 this.collapseBtn.enableDisplayMode();
39356                 /*
39357                 if(c.showPin === true || this.showPin){
39358                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39359                     this.stickBtn.enableDisplayMode();
39360                     this.stickBtn.on("click", this.expand, this);
39361                     this.stickBtn.hide();
39362                 }
39363                 
39364             }
39365             */
39366             /** This region's collapsed element
39367             * @type Roo.Element */
39368             /*
39369              *
39370             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39371                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39372             ]}, true);
39373             
39374             if(c.floatable !== false){
39375                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39376                this.collapsedEl.on("click", this.collapseClick, this);
39377             }
39378
39379             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39380                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39381                    id: "message", unselectable: "on", style:{"float":"left"}});
39382                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39383              }
39384             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39385             this.expandBtn.on("click", this.expand, this);
39386             
39387         }
39388         
39389         if(this.collapseBtn){
39390             this.collapseBtn.setVisible(c.collapsible == true);
39391         }
39392         
39393         this.cmargins = c.cmargins || this.cmargins ||
39394                          (this.position == "west" || this.position == "east" ?
39395                              {top: 0, left: 2, right:2, bottom: 0} :
39396                              {top: 2, left: 0, right:0, bottom: 2});
39397         */
39398         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39399         
39400         
39401         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39402         
39403         this.autoScroll = c.autoScroll || false;
39404         
39405         
39406        
39407         
39408         this.duration = c.duration || .30;
39409         this.slideDuration = c.slideDuration || .45;
39410         this.config = c;
39411        
39412     },
39413     /**
39414      * Returns true if this region is currently visible.
39415      * @return {Boolean}
39416      */
39417     isVisible : function(){
39418         return this.visible;
39419     },
39420
39421     /**
39422      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39423      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39424      */
39425     //setCollapsedTitle : function(title){
39426     //    title = title || "&#160;";
39427      //   if(this.collapsedTitleTextEl){
39428       //      this.collapsedTitleTextEl.innerHTML = title;
39429        // }
39430     //},
39431
39432     getBox : function(){
39433         var b;
39434       //  if(!this.collapsed){
39435             b = this.el.getBox(false, true);
39436        // }else{
39437           //  b = this.collapsedEl.getBox(false, true);
39438         //}
39439         return b;
39440     },
39441
39442     getMargins : function(){
39443         return this.margins;
39444         //return this.collapsed ? this.cmargins : this.margins;
39445     },
39446 /*
39447     highlight : function(){
39448         this.el.addClass("x-layout-panel-dragover");
39449     },
39450
39451     unhighlight : function(){
39452         this.el.removeClass("x-layout-panel-dragover");
39453     },
39454 */
39455     updateBox : function(box)
39456     {
39457         if (!this.bodyEl) {
39458             return; // not rendered yet..
39459         }
39460         
39461         this.box = box;
39462         if(!this.collapsed){
39463             this.el.dom.style.left = box.x + "px";
39464             this.el.dom.style.top = box.y + "px";
39465             this.updateBody(box.width, box.height);
39466         }else{
39467             this.collapsedEl.dom.style.left = box.x + "px";
39468             this.collapsedEl.dom.style.top = box.y + "px";
39469             this.collapsedEl.setSize(box.width, box.height);
39470         }
39471         if(this.tabs){
39472             this.tabs.autoSizeTabs();
39473         }
39474     },
39475
39476     updateBody : function(w, h)
39477     {
39478         if(w !== null){
39479             this.el.setWidth(w);
39480             w -= this.el.getBorderWidth("rl");
39481             if(this.config.adjustments){
39482                 w += this.config.adjustments[0];
39483             }
39484         }
39485         if(h !== null && h > 0){
39486             this.el.setHeight(h);
39487             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39488             h -= this.el.getBorderWidth("tb");
39489             if(this.config.adjustments){
39490                 h += this.config.adjustments[1];
39491             }
39492             this.bodyEl.setHeight(h);
39493             if(this.tabs){
39494                 h = this.tabs.syncHeight(h);
39495             }
39496         }
39497         if(this.panelSize){
39498             w = w !== null ? w : this.panelSize.width;
39499             h = h !== null ? h : this.panelSize.height;
39500         }
39501         if(this.activePanel){
39502             var el = this.activePanel.getEl();
39503             w = w !== null ? w : el.getWidth();
39504             h = h !== null ? h : el.getHeight();
39505             this.panelSize = {width: w, height: h};
39506             this.activePanel.setSize(w, h);
39507         }
39508         if(Roo.isIE && this.tabs){
39509             this.tabs.el.repaint();
39510         }
39511     },
39512
39513     /**
39514      * Returns the container element for this region.
39515      * @return {Roo.Element}
39516      */
39517     getEl : function(){
39518         return this.el;
39519     },
39520
39521     /**
39522      * Hides this region.
39523      */
39524     hide : function(){
39525         //if(!this.collapsed){
39526             this.el.dom.style.left = "-2000px";
39527             this.el.hide();
39528         //}else{
39529          //   this.collapsedEl.dom.style.left = "-2000px";
39530          //   this.collapsedEl.hide();
39531        // }
39532         this.visible = false;
39533         this.fireEvent("visibilitychange", this, false);
39534     },
39535
39536     /**
39537      * Shows this region if it was previously hidden.
39538      */
39539     show : function(){
39540         //if(!this.collapsed){
39541             this.el.show();
39542         //}else{
39543         //    this.collapsedEl.show();
39544        // }
39545         this.visible = true;
39546         this.fireEvent("visibilitychange", this, true);
39547     },
39548 /*
39549     closeClicked : function(){
39550         if(this.activePanel){
39551             this.remove(this.activePanel);
39552         }
39553     },
39554
39555     collapseClick : function(e){
39556         if(this.isSlid){
39557            e.stopPropagation();
39558            this.slideIn();
39559         }else{
39560            e.stopPropagation();
39561            this.slideOut();
39562         }
39563     },
39564 */
39565     /**
39566      * Collapses this region.
39567      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39568      */
39569     /*
39570     collapse : function(skipAnim, skipCheck = false){
39571         if(this.collapsed) {
39572             return;
39573         }
39574         
39575         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39576             
39577             this.collapsed = true;
39578             if(this.split){
39579                 this.split.el.hide();
39580             }
39581             if(this.config.animate && skipAnim !== true){
39582                 this.fireEvent("invalidated", this);
39583                 this.animateCollapse();
39584             }else{
39585                 this.el.setLocation(-20000,-20000);
39586                 this.el.hide();
39587                 this.collapsedEl.show();
39588                 this.fireEvent("collapsed", this);
39589                 this.fireEvent("invalidated", this);
39590             }
39591         }
39592         
39593     },
39594 */
39595     animateCollapse : function(){
39596         // overridden
39597     },
39598
39599     /**
39600      * Expands this region if it was previously collapsed.
39601      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39602      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39603      */
39604     /*
39605     expand : function(e, skipAnim){
39606         if(e) {
39607             e.stopPropagation();
39608         }
39609         if(!this.collapsed || this.el.hasActiveFx()) {
39610             return;
39611         }
39612         if(this.isSlid){
39613             this.afterSlideIn();
39614             skipAnim = true;
39615         }
39616         this.collapsed = false;
39617         if(this.config.animate && skipAnim !== true){
39618             this.animateExpand();
39619         }else{
39620             this.el.show();
39621             if(this.split){
39622                 this.split.el.show();
39623             }
39624             this.collapsedEl.setLocation(-2000,-2000);
39625             this.collapsedEl.hide();
39626             this.fireEvent("invalidated", this);
39627             this.fireEvent("expanded", this);
39628         }
39629     },
39630 */
39631     animateExpand : function(){
39632         // overridden
39633     },
39634
39635     initTabs : function()
39636     {
39637         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39638         
39639         var ts = new Roo.bootstrap.panel.Tabs({
39640             el: this.bodyEl.dom,
39641             region : this,
39642             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39643             disableTooltips: this.config.disableTabTips,
39644             toolbar : this.config.toolbar
39645         });
39646         
39647         if(this.config.hideTabs){
39648             ts.stripWrap.setDisplayed(false);
39649         }
39650         this.tabs = ts;
39651         ts.resizeTabs = this.config.resizeTabs === true;
39652         ts.minTabWidth = this.config.minTabWidth || 40;
39653         ts.maxTabWidth = this.config.maxTabWidth || 250;
39654         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39655         ts.monitorResize = false;
39656         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39657         ts.bodyEl.addClass('roo-layout-tabs-body');
39658         this.panels.each(this.initPanelAsTab, this);
39659     },
39660
39661     initPanelAsTab : function(panel){
39662         var ti = this.tabs.addTab(
39663             panel.getEl().id,
39664             panel.getTitle(),
39665             null,
39666             this.config.closeOnTab && panel.isClosable(),
39667             panel.tpl
39668         );
39669         if(panel.tabTip !== undefined){
39670             ti.setTooltip(panel.tabTip);
39671         }
39672         ti.on("activate", function(){
39673               this.setActivePanel(panel);
39674         }, this);
39675         
39676         if(this.config.closeOnTab){
39677             ti.on("beforeclose", function(t, e){
39678                 e.cancel = true;
39679                 this.remove(panel);
39680             }, this);
39681         }
39682         
39683         panel.tabItem = ti;
39684         
39685         return ti;
39686     },
39687
39688     updatePanelTitle : function(panel, title)
39689     {
39690         if(this.activePanel == panel){
39691             this.updateTitle(title);
39692         }
39693         if(this.tabs){
39694             var ti = this.tabs.getTab(panel.getEl().id);
39695             ti.setText(title);
39696             if(panel.tabTip !== undefined){
39697                 ti.setTooltip(panel.tabTip);
39698             }
39699         }
39700     },
39701
39702     updateTitle : function(title){
39703         if(this.titleTextEl && !this.config.title){
39704             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39705         }
39706     },
39707
39708     setActivePanel : function(panel)
39709     {
39710         panel = this.getPanel(panel);
39711         if(this.activePanel && this.activePanel != panel){
39712             if(this.activePanel.setActiveState(false) === false){
39713                 return;
39714             }
39715         }
39716         this.activePanel = panel;
39717         panel.setActiveState(true);
39718         if(this.panelSize){
39719             panel.setSize(this.panelSize.width, this.panelSize.height);
39720         }
39721         if(this.closeBtn){
39722             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39723         }
39724         this.updateTitle(panel.getTitle());
39725         if(this.tabs){
39726             this.fireEvent("invalidated", this);
39727         }
39728         this.fireEvent("panelactivated", this, panel);
39729     },
39730
39731     /**
39732      * Shows the specified panel.
39733      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39734      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39735      */
39736     showPanel : function(panel)
39737     {
39738         panel = this.getPanel(panel);
39739         if(panel){
39740             if(this.tabs){
39741                 var tab = this.tabs.getTab(panel.getEl().id);
39742                 if(tab.isHidden()){
39743                     this.tabs.unhideTab(tab.id);
39744                 }
39745                 tab.activate();
39746             }else{
39747                 this.setActivePanel(panel);
39748             }
39749         }
39750         return panel;
39751     },
39752
39753     /**
39754      * Get the active panel for this region.
39755      * @return {Roo.ContentPanel} The active panel or null
39756      */
39757     getActivePanel : function(){
39758         return this.activePanel;
39759     },
39760
39761     validateVisibility : function(){
39762         if(this.panels.getCount() < 1){
39763             this.updateTitle("&#160;");
39764             this.closeBtn.hide();
39765             this.hide();
39766         }else{
39767             if(!this.isVisible()){
39768                 this.show();
39769             }
39770         }
39771     },
39772
39773     /**
39774      * Adds the passed ContentPanel(s) to this region.
39775      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39776      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39777      */
39778     add : function(panel)
39779     {
39780         if(arguments.length > 1){
39781             for(var i = 0, len = arguments.length; i < len; i++) {
39782                 this.add(arguments[i]);
39783             }
39784             return null;
39785         }
39786         
39787         // if we have not been rendered yet, then we can not really do much of this..
39788         if (!this.bodyEl) {
39789             this.unrendered_panels.push(panel);
39790             return panel;
39791         }
39792         
39793         
39794         
39795         
39796         if(this.hasPanel(panel)){
39797             this.showPanel(panel);
39798             return panel;
39799         }
39800         panel.setRegion(this);
39801         this.panels.add(panel);
39802        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39803             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39804             // and hide them... ???
39805             this.bodyEl.dom.appendChild(panel.getEl().dom);
39806             if(panel.background !== true){
39807                 this.setActivePanel(panel);
39808             }
39809             this.fireEvent("paneladded", this, panel);
39810             return panel;
39811         }
39812         */
39813         if(!this.tabs){
39814             this.initTabs();
39815         }else{
39816             this.initPanelAsTab(panel);
39817         }
39818         
39819         
39820         if(panel.background !== true){
39821             this.tabs.activate(panel.getEl().id);
39822         }
39823         this.fireEvent("paneladded", this, panel);
39824         return panel;
39825     },
39826
39827     /**
39828      * Hides the tab for the specified panel.
39829      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39830      */
39831     hidePanel : function(panel){
39832         if(this.tabs && (panel = this.getPanel(panel))){
39833             this.tabs.hideTab(panel.getEl().id);
39834         }
39835     },
39836
39837     /**
39838      * Unhides the tab for a previously hidden panel.
39839      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39840      */
39841     unhidePanel : function(panel){
39842         if(this.tabs && (panel = this.getPanel(panel))){
39843             this.tabs.unhideTab(panel.getEl().id);
39844         }
39845     },
39846
39847     clearPanels : function(){
39848         while(this.panels.getCount() > 0){
39849              this.remove(this.panels.first());
39850         }
39851     },
39852
39853     /**
39854      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39855      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39856      * @param {Boolean} preservePanel Overrides the config preservePanel option
39857      * @return {Roo.ContentPanel} The panel that was removed
39858      */
39859     remove : function(panel, preservePanel)
39860     {
39861         panel = this.getPanel(panel);
39862         if(!panel){
39863             return null;
39864         }
39865         var e = {};
39866         this.fireEvent("beforeremove", this, panel, e);
39867         if(e.cancel === true){
39868             return null;
39869         }
39870         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39871         var panelId = panel.getId();
39872         this.panels.removeKey(panelId);
39873         if(preservePanel){
39874             document.body.appendChild(panel.getEl().dom);
39875         }
39876         if(this.tabs){
39877             this.tabs.removeTab(panel.getEl().id);
39878         }else if (!preservePanel){
39879             this.bodyEl.dom.removeChild(panel.getEl().dom);
39880         }
39881         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39882             var p = this.panels.first();
39883             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39884             tempEl.appendChild(p.getEl().dom);
39885             this.bodyEl.update("");
39886             this.bodyEl.dom.appendChild(p.getEl().dom);
39887             tempEl = null;
39888             this.updateTitle(p.getTitle());
39889             this.tabs = null;
39890             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39891             this.setActivePanel(p);
39892         }
39893         panel.setRegion(null);
39894         if(this.activePanel == panel){
39895             this.activePanel = null;
39896         }
39897         if(this.config.autoDestroy !== false && preservePanel !== true){
39898             try{panel.destroy();}catch(e){}
39899         }
39900         this.fireEvent("panelremoved", this, panel);
39901         return panel;
39902     },
39903
39904     /**
39905      * Returns the TabPanel component used by this region
39906      * @return {Roo.TabPanel}
39907      */
39908     getTabs : function(){
39909         return this.tabs;
39910     },
39911
39912     createTool : function(parentEl, className){
39913         var btn = Roo.DomHelper.append(parentEl, {
39914             tag: "div",
39915             cls: "x-layout-tools-button",
39916             children: [ {
39917                 tag: "div",
39918                 cls: "roo-layout-tools-button-inner " + className,
39919                 html: "&#160;"
39920             }]
39921         }, true);
39922         btn.addClassOnOver("roo-layout-tools-button-over");
39923         return btn;
39924     }
39925 });/*
39926  * Based on:
39927  * Ext JS Library 1.1.1
39928  * Copyright(c) 2006-2007, Ext JS, LLC.
39929  *
39930  * Originally Released Under LGPL - original licence link has changed is not relivant.
39931  *
39932  * Fork - LGPL
39933  * <script type="text/javascript">
39934  */
39935  
39936
39937
39938 /**
39939  * @class Roo.SplitLayoutRegion
39940  * @extends Roo.LayoutRegion
39941  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39942  */
39943 Roo.bootstrap.layout.Split = function(config){
39944     this.cursor = config.cursor;
39945     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39946 };
39947
39948 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39949 {
39950     splitTip : "Drag to resize.",
39951     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39952     useSplitTips : false,
39953
39954     applyConfig : function(config){
39955         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39956     },
39957     
39958     onRender : function(ctr,pos) {
39959         
39960         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39961         if(!this.config.split){
39962             return;
39963         }
39964         if(!this.split){
39965             
39966             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39967                             tag: "div",
39968                             id: this.el.id + "-split",
39969                             cls: "roo-layout-split roo-layout-split-"+this.position,
39970                             html: "&#160;"
39971             });
39972             /** The SplitBar for this region 
39973             * @type Roo.SplitBar */
39974             // does not exist yet...
39975             Roo.log([this.position, this.orientation]);
39976             
39977             this.split = new Roo.bootstrap.SplitBar({
39978                 dragElement : splitEl,
39979                 resizingElement: this.el,
39980                 orientation : this.orientation
39981             });
39982             
39983             this.split.on("moved", this.onSplitMove, this);
39984             this.split.useShim = this.config.useShim === true;
39985             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39986             if(this.useSplitTips){
39987                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39988             }
39989             //if(config.collapsible){
39990             //    this.split.el.on("dblclick", this.collapse,  this);
39991             //}
39992         }
39993         if(typeof this.config.minSize != "undefined"){
39994             this.split.minSize = this.config.minSize;
39995         }
39996         if(typeof this.config.maxSize != "undefined"){
39997             this.split.maxSize = this.config.maxSize;
39998         }
39999         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
40000             this.hideSplitter();
40001         }
40002         
40003     },
40004
40005     getHMaxSize : function(){
40006          var cmax = this.config.maxSize || 10000;
40007          var center = this.mgr.getRegion("center");
40008          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40009     },
40010
40011     getVMaxSize : function(){
40012          var cmax = this.config.maxSize || 10000;
40013          var center = this.mgr.getRegion("center");
40014          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40015     },
40016
40017     onSplitMove : function(split, newSize){
40018         this.fireEvent("resized", this, newSize);
40019     },
40020     
40021     /** 
40022      * Returns the {@link Roo.SplitBar} for this region.
40023      * @return {Roo.SplitBar}
40024      */
40025     getSplitBar : function(){
40026         return this.split;
40027     },
40028     
40029     hide : function(){
40030         this.hideSplitter();
40031         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40032     },
40033
40034     hideSplitter : function(){
40035         if(this.split){
40036             this.split.el.setLocation(-2000,-2000);
40037             this.split.el.hide();
40038         }
40039     },
40040
40041     show : function(){
40042         if(this.split){
40043             this.split.el.show();
40044         }
40045         Roo.bootstrap.layout.Split.superclass.show.call(this);
40046     },
40047     
40048     beforeSlide: function(){
40049         if(Roo.isGecko){// firefox overflow auto bug workaround
40050             this.bodyEl.clip();
40051             if(this.tabs) {
40052                 this.tabs.bodyEl.clip();
40053             }
40054             if(this.activePanel){
40055                 this.activePanel.getEl().clip();
40056                 
40057                 if(this.activePanel.beforeSlide){
40058                     this.activePanel.beforeSlide();
40059                 }
40060             }
40061         }
40062     },
40063     
40064     afterSlide : function(){
40065         if(Roo.isGecko){// firefox overflow auto bug workaround
40066             this.bodyEl.unclip();
40067             if(this.tabs) {
40068                 this.tabs.bodyEl.unclip();
40069             }
40070             if(this.activePanel){
40071                 this.activePanel.getEl().unclip();
40072                 if(this.activePanel.afterSlide){
40073                     this.activePanel.afterSlide();
40074                 }
40075             }
40076         }
40077     },
40078
40079     initAutoHide : function(){
40080         if(this.autoHide !== false){
40081             if(!this.autoHideHd){
40082                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40083                 this.autoHideHd = {
40084                     "mouseout": function(e){
40085                         if(!e.within(this.el, true)){
40086                             st.delay(500);
40087                         }
40088                     },
40089                     "mouseover" : function(e){
40090                         st.cancel();
40091                     },
40092                     scope : this
40093                 };
40094             }
40095             this.el.on(this.autoHideHd);
40096         }
40097     },
40098
40099     clearAutoHide : function(){
40100         if(this.autoHide !== false){
40101             this.el.un("mouseout", this.autoHideHd.mouseout);
40102             this.el.un("mouseover", this.autoHideHd.mouseover);
40103         }
40104     },
40105
40106     clearMonitor : function(){
40107         Roo.get(document).un("click", this.slideInIf, this);
40108     },
40109
40110     // these names are backwards but not changed for compat
40111     slideOut : function(){
40112         if(this.isSlid || this.el.hasActiveFx()){
40113             return;
40114         }
40115         this.isSlid = true;
40116         if(this.collapseBtn){
40117             this.collapseBtn.hide();
40118         }
40119         this.closeBtnState = this.closeBtn.getStyle('display');
40120         this.closeBtn.hide();
40121         if(this.stickBtn){
40122             this.stickBtn.show();
40123         }
40124         this.el.show();
40125         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40126         this.beforeSlide();
40127         this.el.setStyle("z-index", 10001);
40128         this.el.slideIn(this.getSlideAnchor(), {
40129             callback: function(){
40130                 this.afterSlide();
40131                 this.initAutoHide();
40132                 Roo.get(document).on("click", this.slideInIf, this);
40133                 this.fireEvent("slideshow", this);
40134             },
40135             scope: this,
40136             block: true
40137         });
40138     },
40139
40140     afterSlideIn : function(){
40141         this.clearAutoHide();
40142         this.isSlid = false;
40143         this.clearMonitor();
40144         this.el.setStyle("z-index", "");
40145         if(this.collapseBtn){
40146             this.collapseBtn.show();
40147         }
40148         this.closeBtn.setStyle('display', this.closeBtnState);
40149         if(this.stickBtn){
40150             this.stickBtn.hide();
40151         }
40152         this.fireEvent("slidehide", this);
40153     },
40154
40155     slideIn : function(cb){
40156         if(!this.isSlid || this.el.hasActiveFx()){
40157             Roo.callback(cb);
40158             return;
40159         }
40160         this.isSlid = false;
40161         this.beforeSlide();
40162         this.el.slideOut(this.getSlideAnchor(), {
40163             callback: function(){
40164                 this.el.setLeftTop(-10000, -10000);
40165                 this.afterSlide();
40166                 this.afterSlideIn();
40167                 Roo.callback(cb);
40168             },
40169             scope: this,
40170             block: true
40171         });
40172     },
40173     
40174     slideInIf : function(e){
40175         if(!e.within(this.el)){
40176             this.slideIn();
40177         }
40178     },
40179
40180     animateCollapse : function(){
40181         this.beforeSlide();
40182         this.el.setStyle("z-index", 20000);
40183         var anchor = this.getSlideAnchor();
40184         this.el.slideOut(anchor, {
40185             callback : function(){
40186                 this.el.setStyle("z-index", "");
40187                 this.collapsedEl.slideIn(anchor, {duration:.3});
40188                 this.afterSlide();
40189                 this.el.setLocation(-10000,-10000);
40190                 this.el.hide();
40191                 this.fireEvent("collapsed", this);
40192             },
40193             scope: this,
40194             block: true
40195         });
40196     },
40197
40198     animateExpand : function(){
40199         this.beforeSlide();
40200         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40201         this.el.setStyle("z-index", 20000);
40202         this.collapsedEl.hide({
40203             duration:.1
40204         });
40205         this.el.slideIn(this.getSlideAnchor(), {
40206             callback : function(){
40207                 this.el.setStyle("z-index", "");
40208                 this.afterSlide();
40209                 if(this.split){
40210                     this.split.el.show();
40211                 }
40212                 this.fireEvent("invalidated", this);
40213                 this.fireEvent("expanded", this);
40214             },
40215             scope: this,
40216             block: true
40217         });
40218     },
40219
40220     anchors : {
40221         "west" : "left",
40222         "east" : "right",
40223         "north" : "top",
40224         "south" : "bottom"
40225     },
40226
40227     sanchors : {
40228         "west" : "l",
40229         "east" : "r",
40230         "north" : "t",
40231         "south" : "b"
40232     },
40233
40234     canchors : {
40235         "west" : "tl-tr",
40236         "east" : "tr-tl",
40237         "north" : "tl-bl",
40238         "south" : "bl-tl"
40239     },
40240
40241     getAnchor : function(){
40242         return this.anchors[this.position];
40243     },
40244
40245     getCollapseAnchor : function(){
40246         return this.canchors[this.position];
40247     },
40248
40249     getSlideAnchor : function(){
40250         return this.sanchors[this.position];
40251     },
40252
40253     getAlignAdj : function(){
40254         var cm = this.cmargins;
40255         switch(this.position){
40256             case "west":
40257                 return [0, 0];
40258             break;
40259             case "east":
40260                 return [0, 0];
40261             break;
40262             case "north":
40263                 return [0, 0];
40264             break;
40265             case "south":
40266                 return [0, 0];
40267             break;
40268         }
40269     },
40270
40271     getExpandAdj : function(){
40272         var c = this.collapsedEl, cm = this.cmargins;
40273         switch(this.position){
40274             case "west":
40275                 return [-(cm.right+c.getWidth()+cm.left), 0];
40276             break;
40277             case "east":
40278                 return [cm.right+c.getWidth()+cm.left, 0];
40279             break;
40280             case "north":
40281                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40282             break;
40283             case "south":
40284                 return [0, cm.top+cm.bottom+c.getHeight()];
40285             break;
40286         }
40287     }
40288 });/*
40289  * Based on:
40290  * Ext JS Library 1.1.1
40291  * Copyright(c) 2006-2007, Ext JS, LLC.
40292  *
40293  * Originally Released Under LGPL - original licence link has changed is not relivant.
40294  *
40295  * Fork - LGPL
40296  * <script type="text/javascript">
40297  */
40298 /*
40299  * These classes are private internal classes
40300  */
40301 Roo.bootstrap.layout.Center = function(config){
40302     config.region = "center";
40303     Roo.bootstrap.layout.Region.call(this, config);
40304     this.visible = true;
40305     this.minWidth = config.minWidth || 20;
40306     this.minHeight = config.minHeight || 20;
40307 };
40308
40309 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40310     hide : function(){
40311         // center panel can't be hidden
40312     },
40313     
40314     show : function(){
40315         // center panel can't be hidden
40316     },
40317     
40318     getMinWidth: function(){
40319         return this.minWidth;
40320     },
40321     
40322     getMinHeight: function(){
40323         return this.minHeight;
40324     }
40325 });
40326
40327
40328
40329
40330  
40331
40332
40333
40334
40335
40336
40337 Roo.bootstrap.layout.North = function(config)
40338 {
40339     config.region = 'north';
40340     config.cursor = 'n-resize';
40341     
40342     Roo.bootstrap.layout.Split.call(this, config);
40343     
40344     
40345     if(this.split){
40346         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40347         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40348         this.split.el.addClass("roo-layout-split-v");
40349     }
40350     //var size = config.initialSize || config.height;
40351     //if(this.el && typeof size != "undefined"){
40352     //    this.el.setHeight(size);
40353     //}
40354 };
40355 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40356 {
40357     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40358      
40359      
40360     onRender : function(ctr, pos)
40361     {
40362         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40363         var size = this.config.initialSize || this.config.height;
40364         if(this.el && typeof size != "undefined"){
40365             this.el.setHeight(size);
40366         }
40367     
40368     },
40369     
40370     getBox : function(){
40371         if(this.collapsed){
40372             return this.collapsedEl.getBox();
40373         }
40374         var box = this.el.getBox();
40375         if(this.split){
40376             box.height += this.split.el.getHeight();
40377         }
40378         return box;
40379     },
40380     
40381     updateBox : function(box){
40382         if(this.split && !this.collapsed){
40383             box.height -= this.split.el.getHeight();
40384             this.split.el.setLeft(box.x);
40385             this.split.el.setTop(box.y+box.height);
40386             this.split.el.setWidth(box.width);
40387         }
40388         if(this.collapsed){
40389             this.updateBody(box.width, null);
40390         }
40391         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40392     }
40393 });
40394
40395
40396
40397
40398
40399 Roo.bootstrap.layout.South = function(config){
40400     config.region = 'south';
40401     config.cursor = 's-resize';
40402     Roo.bootstrap.layout.Split.call(this, config);
40403     if(this.split){
40404         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40405         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40406         this.split.el.addClass("roo-layout-split-v");
40407     }
40408     
40409 };
40410
40411 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40412     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40413     
40414     onRender : function(ctr, pos)
40415     {
40416         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40417         var size = this.config.initialSize || this.config.height;
40418         if(this.el && typeof size != "undefined"){
40419             this.el.setHeight(size);
40420         }
40421     
40422     },
40423     
40424     getBox : function(){
40425         if(this.collapsed){
40426             return this.collapsedEl.getBox();
40427         }
40428         var box = this.el.getBox();
40429         if(this.split){
40430             var sh = this.split.el.getHeight();
40431             box.height += sh;
40432             box.y -= sh;
40433         }
40434         return box;
40435     },
40436     
40437     updateBox : function(box){
40438         if(this.split && !this.collapsed){
40439             var sh = this.split.el.getHeight();
40440             box.height -= sh;
40441             box.y += sh;
40442             this.split.el.setLeft(box.x);
40443             this.split.el.setTop(box.y-sh);
40444             this.split.el.setWidth(box.width);
40445         }
40446         if(this.collapsed){
40447             this.updateBody(box.width, null);
40448         }
40449         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40450     }
40451 });
40452
40453 Roo.bootstrap.layout.East = function(config){
40454     config.region = "east";
40455     config.cursor = "e-resize";
40456     Roo.bootstrap.layout.Split.call(this, config);
40457     if(this.split){
40458         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40459         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40460         this.split.el.addClass("roo-layout-split-h");
40461     }
40462     
40463 };
40464 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40465     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40466     
40467     onRender : function(ctr, pos)
40468     {
40469         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40470         var size = this.config.initialSize || this.config.width;
40471         if(this.el && typeof size != "undefined"){
40472             this.el.setWidth(size);
40473         }
40474     
40475     },
40476     
40477     getBox : function(){
40478         if(this.collapsed){
40479             return this.collapsedEl.getBox();
40480         }
40481         var box = this.el.getBox();
40482         if(this.split){
40483             var sw = this.split.el.getWidth();
40484             box.width += sw;
40485             box.x -= sw;
40486         }
40487         return box;
40488     },
40489
40490     updateBox : function(box){
40491         if(this.split && !this.collapsed){
40492             var sw = this.split.el.getWidth();
40493             box.width -= sw;
40494             this.split.el.setLeft(box.x);
40495             this.split.el.setTop(box.y);
40496             this.split.el.setHeight(box.height);
40497             box.x += sw;
40498         }
40499         if(this.collapsed){
40500             this.updateBody(null, box.height);
40501         }
40502         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40503     }
40504 });
40505
40506 Roo.bootstrap.layout.West = function(config){
40507     config.region = "west";
40508     config.cursor = "w-resize";
40509     
40510     Roo.bootstrap.layout.Split.call(this, config);
40511     if(this.split){
40512         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40513         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40514         this.split.el.addClass("roo-layout-split-h");
40515     }
40516     
40517 };
40518 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40519     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40520     
40521     onRender: function(ctr, pos)
40522     {
40523         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40524         var size = this.config.initialSize || this.config.width;
40525         if(typeof size != "undefined"){
40526             this.el.setWidth(size);
40527         }
40528     },
40529     
40530     getBox : function(){
40531         if(this.collapsed){
40532             return this.collapsedEl.getBox();
40533         }
40534         var box = this.el.getBox();
40535         if (box.width == 0) {
40536             box.width = this.config.width; // kludge?
40537         }
40538         if(this.split){
40539             box.width += this.split.el.getWidth();
40540         }
40541         return box;
40542     },
40543     
40544     updateBox : function(box){
40545         if(this.split && !this.collapsed){
40546             var sw = this.split.el.getWidth();
40547             box.width -= sw;
40548             this.split.el.setLeft(box.x+box.width);
40549             this.split.el.setTop(box.y);
40550             this.split.el.setHeight(box.height);
40551         }
40552         if(this.collapsed){
40553             this.updateBody(null, box.height);
40554         }
40555         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40556     }
40557 });Roo.namespace("Roo.bootstrap.panel");/*
40558  * Based on:
40559  * Ext JS Library 1.1.1
40560  * Copyright(c) 2006-2007, Ext JS, LLC.
40561  *
40562  * Originally Released Under LGPL - original licence link has changed is not relivant.
40563  *
40564  * Fork - LGPL
40565  * <script type="text/javascript">
40566  */
40567 /**
40568  * @class Roo.ContentPanel
40569  * @extends Roo.util.Observable
40570  * @builder-top
40571  * A basic ContentPanel element.
40572  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40573  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40574  * @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
40575  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40576  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40577  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40578  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40579  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40580  * @cfg {String} title          The title for this panel
40581  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40582  * @cfg {String} url            Calls {@link #setUrl} with this value
40583  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40584  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40585  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40586  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40587  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40588  * @cfg {Boolean} badges render the badges
40589  * @cfg {String} cls  extra classes to use  
40590  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40591
40592  * @constructor
40593  * Create a new ContentPanel.
40594  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40595  * @param {String/Object} config A string to set only the title or a config object
40596  * @param {String} content (optional) Set the HTML content for this panel
40597  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40598  */
40599 Roo.bootstrap.panel.Content = function( config){
40600     
40601     this.tpl = config.tpl || false;
40602     
40603     var el = config.el;
40604     var content = config.content;
40605
40606     if(config.autoCreate){ // xtype is available if this is called from factory
40607         el = Roo.id();
40608     }
40609     this.el = Roo.get(el);
40610     if(!this.el && config && config.autoCreate){
40611         if(typeof config.autoCreate == "object"){
40612             if(!config.autoCreate.id){
40613                 config.autoCreate.id = config.id||el;
40614             }
40615             this.el = Roo.DomHelper.append(document.body,
40616                         config.autoCreate, true);
40617         }else{
40618             var elcfg =  {
40619                 tag: "div",
40620                 cls: (config.cls || '') +
40621                     (config.background ? ' bg-' + config.background : '') +
40622                     " roo-layout-inactive-content",
40623                 id: config.id||el
40624             };
40625             if (config.iframe) {
40626                 elcfg.cn = [
40627                     {
40628                         tag : 'iframe',
40629                         style : 'border: 0px',
40630                         src : 'about:blank'
40631                     }
40632                 ];
40633             }
40634               
40635             if (config.html) {
40636                 elcfg.html = config.html;
40637                 
40638             }
40639                         
40640             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40641             if (config.iframe) {
40642                 this.iframeEl = this.el.select('iframe',true).first();
40643             }
40644             
40645         }
40646     } 
40647     this.closable = false;
40648     this.loaded = false;
40649     this.active = false;
40650    
40651       
40652     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40653         
40654         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40655         
40656         this.wrapEl = this.el; //this.el.wrap();
40657         var ti = [];
40658         if (config.toolbar.items) {
40659             ti = config.toolbar.items ;
40660             delete config.toolbar.items ;
40661         }
40662         
40663         var nitems = [];
40664         this.toolbar.render(this.wrapEl, 'before');
40665         for(var i =0;i < ti.length;i++) {
40666           //  Roo.log(['add child', items[i]]);
40667             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40668         }
40669         this.toolbar.items = nitems;
40670         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40671         delete config.toolbar;
40672         
40673     }
40674     /*
40675     // xtype created footer. - not sure if will work as we normally have to render first..
40676     if (this.footer && !this.footer.el && this.footer.xtype) {
40677         if (!this.wrapEl) {
40678             this.wrapEl = this.el.wrap();
40679         }
40680     
40681         this.footer.container = this.wrapEl.createChild();
40682          
40683         this.footer = Roo.factory(this.footer, Roo);
40684         
40685     }
40686     */
40687     
40688      if(typeof config == "string"){
40689         this.title = config;
40690     }else{
40691         Roo.apply(this, config);
40692     }
40693     
40694     if(this.resizeEl){
40695         this.resizeEl = Roo.get(this.resizeEl, true);
40696     }else{
40697         this.resizeEl = this.el;
40698     }
40699     // handle view.xtype
40700     
40701  
40702     
40703     
40704     this.addEvents({
40705         /**
40706          * @event activate
40707          * Fires when this panel is activated. 
40708          * @param {Roo.ContentPanel} this
40709          */
40710         "activate" : true,
40711         /**
40712          * @event deactivate
40713          * Fires when this panel is activated. 
40714          * @param {Roo.ContentPanel} this
40715          */
40716         "deactivate" : true,
40717
40718         /**
40719          * @event resize
40720          * Fires when this panel is resized if fitToFrame is true.
40721          * @param {Roo.ContentPanel} this
40722          * @param {Number} width The width after any component adjustments
40723          * @param {Number} height The height after any component adjustments
40724          */
40725         "resize" : true,
40726         
40727          /**
40728          * @event render
40729          * Fires when this tab is created
40730          * @param {Roo.ContentPanel} this
40731          */
40732         "render" : true,
40733         
40734           /**
40735          * @event scroll
40736          * Fires when this content is scrolled
40737          * @param {Roo.ContentPanel} this
40738          * @param {Event} scrollEvent
40739          */
40740         "scroll" : true
40741         
40742         
40743         
40744     });
40745     
40746
40747     
40748     
40749     if(this.autoScroll && !this.iframe){
40750         this.resizeEl.setStyle("overflow", "auto");
40751         this.resizeEl.on('scroll', this.onScroll, this);
40752     } else {
40753         // fix randome scrolling
40754         //this.el.on('scroll', function() {
40755         //    Roo.log('fix random scolling');
40756         //    this.scrollTo('top',0); 
40757         //});
40758     }
40759     content = content || this.content;
40760     if(content){
40761         this.setContent(content);
40762     }
40763     if(config && config.url){
40764         this.setUrl(this.url, this.params, this.loadOnce);
40765     }
40766     
40767     
40768     
40769     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40770     
40771     if (this.view && typeof(this.view.xtype) != 'undefined') {
40772         this.view.el = this.el.appendChild(document.createElement("div"));
40773         this.view = Roo.factory(this.view); 
40774         this.view.render  &&  this.view.render(false, '');  
40775     }
40776     
40777     
40778     this.fireEvent('render', this);
40779 };
40780
40781 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40782     
40783     cls : '',
40784     background : '',
40785     
40786     tabTip : '',
40787     
40788     iframe : false,
40789     iframeEl : false,
40790     
40791     /* Resize Element - use this to work out scroll etc. */
40792     resizeEl : false,
40793     
40794     setRegion : function(region){
40795         this.region = region;
40796         this.setActiveClass(region && !this.background);
40797     },
40798     
40799     
40800     setActiveClass: function(state)
40801     {
40802         if(state){
40803            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40804            this.el.setStyle('position','relative');
40805         }else{
40806            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40807            this.el.setStyle('position', 'absolute');
40808         } 
40809     },
40810     
40811     /**
40812      * Returns the toolbar for this Panel if one was configured. 
40813      * @return {Roo.Toolbar} 
40814      */
40815     getToolbar : function(){
40816         return this.toolbar;
40817     },
40818     
40819     setActiveState : function(active)
40820     {
40821         this.active = active;
40822         this.setActiveClass(active);
40823         if(!active){
40824             if(this.fireEvent("deactivate", this) === false){
40825                 return false;
40826             }
40827             return true;
40828         }
40829         this.fireEvent("activate", this);
40830         return true;
40831     },
40832     /**
40833      * Updates this panel's element (not for iframe)
40834      * @param {String} content The new content
40835      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40836     */
40837     setContent : function(content, loadScripts){
40838         if (this.iframe) {
40839             return;
40840         }
40841         
40842         this.el.update(content, loadScripts);
40843     },
40844
40845     ignoreResize : function(w, h){
40846         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40847             return true;
40848         }else{
40849             this.lastSize = {width: w, height: h};
40850             return false;
40851         }
40852     },
40853     /**
40854      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40855      * @return {Roo.UpdateManager} The UpdateManager
40856      */
40857     getUpdateManager : function(){
40858         if (this.iframe) {
40859             return false;
40860         }
40861         return this.el.getUpdateManager();
40862     },
40863      /**
40864      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40865      * Does not work with IFRAME contents
40866      * @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:
40867 <pre><code>
40868 panel.load({
40869     url: "your-url.php",
40870     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40871     callback: yourFunction,
40872     scope: yourObject, //(optional scope)
40873     discardUrl: false,
40874     nocache: false,
40875     text: "Loading...",
40876     timeout: 30,
40877     scripts: false
40878 });
40879 </code></pre>
40880      
40881      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40882      * 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.
40883      * @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}
40884      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40885      * @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.
40886      * @return {Roo.ContentPanel} this
40887      */
40888     load : function(){
40889         
40890         if (this.iframe) {
40891             return this;
40892         }
40893         
40894         var um = this.el.getUpdateManager();
40895         um.update.apply(um, arguments);
40896         return this;
40897     },
40898
40899
40900     /**
40901      * 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.
40902      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40903      * @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)
40904      * @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)
40905      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40906      */
40907     setUrl : function(url, params, loadOnce){
40908         if (this.iframe) {
40909             this.iframeEl.dom.src = url;
40910             return false;
40911         }
40912         
40913         if(this.refreshDelegate){
40914             this.removeListener("activate", this.refreshDelegate);
40915         }
40916         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40917         this.on("activate", this.refreshDelegate);
40918         return this.el.getUpdateManager();
40919     },
40920     
40921     _handleRefresh : function(url, params, loadOnce){
40922         if(!loadOnce || !this.loaded){
40923             var updater = this.el.getUpdateManager();
40924             updater.update(url, params, this._setLoaded.createDelegate(this));
40925         }
40926     },
40927     
40928     _setLoaded : function(){
40929         this.loaded = true;
40930     }, 
40931     
40932     /**
40933      * Returns this panel's id
40934      * @return {String} 
40935      */
40936     getId : function(){
40937         return this.el.id;
40938     },
40939     
40940     /** 
40941      * Returns this panel's element - used by regiosn to add.
40942      * @return {Roo.Element} 
40943      */
40944     getEl : function(){
40945         return this.wrapEl || this.el;
40946     },
40947     
40948    
40949     
40950     adjustForComponents : function(width, height)
40951     {
40952         //Roo.log('adjustForComponents ');
40953         if(this.resizeEl != this.el){
40954             width -= this.el.getFrameWidth('lr');
40955             height -= this.el.getFrameWidth('tb');
40956         }
40957         if(this.toolbar){
40958             var te = this.toolbar.getEl();
40959             te.setWidth(width);
40960             height -= te.getHeight();
40961         }
40962         if(this.footer){
40963             var te = this.footer.getEl();
40964             te.setWidth(width);
40965             height -= te.getHeight();
40966         }
40967         
40968         
40969         if(this.adjustments){
40970             width += this.adjustments[0];
40971             height += this.adjustments[1];
40972         }
40973         return {"width": width, "height": height};
40974     },
40975     
40976     setSize : function(width, height){
40977         if(this.fitToFrame && !this.ignoreResize(width, height)){
40978             if(this.fitContainer && this.resizeEl != this.el){
40979                 this.el.setSize(width, height);
40980             }
40981             var size = this.adjustForComponents(width, height);
40982             if (this.iframe) {
40983                 this.iframeEl.setSize(width,height);
40984             }
40985             
40986             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40987             this.fireEvent('resize', this, size.width, size.height);
40988             
40989             
40990         }
40991     },
40992     
40993     /**
40994      * Returns this panel's title
40995      * @return {String} 
40996      */
40997     getTitle : function(){
40998         
40999         if (typeof(this.title) != 'object') {
41000             return this.title;
41001         }
41002         
41003         var t = '';
41004         for (var k in this.title) {
41005             if (!this.title.hasOwnProperty(k)) {
41006                 continue;
41007             }
41008             
41009             if (k.indexOf('-') >= 0) {
41010                 var s = k.split('-');
41011                 for (var i = 0; i<s.length; i++) {
41012                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41013                 }
41014             } else {
41015                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41016             }
41017         }
41018         return t;
41019     },
41020     
41021     /**
41022      * Set this panel's title
41023      * @param {String} title
41024      */
41025     setTitle : function(title){
41026         this.title = title;
41027         if(this.region){
41028             this.region.updatePanelTitle(this, title);
41029         }
41030     },
41031     
41032     /**
41033      * Returns true is this panel was configured to be closable
41034      * @return {Boolean} 
41035      */
41036     isClosable : function(){
41037         return this.closable;
41038     },
41039     
41040     beforeSlide : function(){
41041         this.el.clip();
41042         this.resizeEl.clip();
41043     },
41044     
41045     afterSlide : function(){
41046         this.el.unclip();
41047         this.resizeEl.unclip();
41048     },
41049     
41050     /**
41051      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41052      *   Will fail silently if the {@link #setUrl} method has not been called.
41053      *   This does not activate the panel, just updates its content.
41054      */
41055     refresh : function(){
41056         if(this.refreshDelegate){
41057            this.loaded = false;
41058            this.refreshDelegate();
41059         }
41060     },
41061     
41062     /**
41063      * Destroys this panel
41064      */
41065     destroy : function(){
41066         this.el.removeAllListeners();
41067         var tempEl = document.createElement("span");
41068         tempEl.appendChild(this.el.dom);
41069         tempEl.innerHTML = "";
41070         this.el.remove();
41071         this.el = null;
41072     },
41073     
41074     /**
41075      * form - if the content panel contains a form - this is a reference to it.
41076      * @type {Roo.form.Form}
41077      */
41078     form : false,
41079     /**
41080      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41081      *    This contains a reference to it.
41082      * @type {Roo.View}
41083      */
41084     view : false,
41085     
41086       /**
41087      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41088      * <pre><code>
41089
41090 layout.addxtype({
41091        xtype : 'Form',
41092        items: [ .... ]
41093    }
41094 );
41095
41096 </code></pre>
41097      * @param {Object} cfg Xtype definition of item to add.
41098      */
41099     
41100     
41101     getChildContainer: function () {
41102         return this.getEl();
41103     },
41104     
41105     
41106     onScroll : function(e)
41107     {
41108         this.fireEvent('scroll', this, e);
41109     }
41110     
41111     
41112     /*
41113         var  ret = new Roo.factory(cfg);
41114         return ret;
41115         
41116         
41117         // add form..
41118         if (cfg.xtype.match(/^Form$/)) {
41119             
41120             var el;
41121             //if (this.footer) {
41122             //    el = this.footer.container.insertSibling(false, 'before');
41123             //} else {
41124                 el = this.el.createChild();
41125             //}
41126
41127             this.form = new  Roo.form.Form(cfg);
41128             
41129             
41130             if ( this.form.allItems.length) {
41131                 this.form.render(el.dom);
41132             }
41133             return this.form;
41134         }
41135         // should only have one of theses..
41136         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41137             // views.. should not be just added - used named prop 'view''
41138             
41139             cfg.el = this.el.appendChild(document.createElement("div"));
41140             // factory?
41141             
41142             var ret = new Roo.factory(cfg);
41143              
41144              ret.render && ret.render(false, ''); // render blank..
41145             this.view = ret;
41146             return ret;
41147         }
41148         return false;
41149     }
41150     \*/
41151 });
41152  
41153 /**
41154  * @class Roo.bootstrap.panel.Grid
41155  * @extends Roo.bootstrap.panel.Content
41156  * @constructor
41157  * Create a new GridPanel.
41158  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41159  * @param {Object} config A the config object
41160   
41161  */
41162
41163
41164
41165 Roo.bootstrap.panel.Grid = function(config)
41166 {
41167     
41168       
41169     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41170         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41171
41172     config.el = this.wrapper;
41173     //this.el = this.wrapper;
41174     
41175       if (config.container) {
41176         // ctor'ed from a Border/panel.grid
41177         
41178         
41179         this.wrapper.setStyle("overflow", "hidden");
41180         this.wrapper.addClass('roo-grid-container');
41181
41182     }
41183     
41184     
41185     if(config.toolbar){
41186         var tool_el = this.wrapper.createChild();    
41187         this.toolbar = Roo.factory(config.toolbar);
41188         var ti = [];
41189         if (config.toolbar.items) {
41190             ti = config.toolbar.items ;
41191             delete config.toolbar.items ;
41192         }
41193         
41194         var nitems = [];
41195         this.toolbar.render(tool_el);
41196         for(var i =0;i < ti.length;i++) {
41197           //  Roo.log(['add child', items[i]]);
41198             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41199         }
41200         this.toolbar.items = nitems;
41201         
41202         delete config.toolbar;
41203     }
41204     
41205     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41206     config.grid.scrollBody = true;;
41207     config.grid.monitorWindowResize = false; // turn off autosizing
41208     config.grid.autoHeight = false;
41209     config.grid.autoWidth = false;
41210     
41211     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41212     
41213     if (config.background) {
41214         // render grid on panel activation (if panel background)
41215         this.on('activate', function(gp) {
41216             if (!gp.grid.rendered) {
41217                 gp.grid.render(this.wrapper);
41218                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41219             }
41220         });
41221             
41222     } else {
41223         this.grid.render(this.wrapper);
41224         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41225
41226     }
41227     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41228     // ??? needed ??? config.el = this.wrapper;
41229     
41230     
41231     
41232   
41233     // xtype created footer. - not sure if will work as we normally have to render first..
41234     if (this.footer && !this.footer.el && this.footer.xtype) {
41235         
41236         var ctr = this.grid.getView().getFooterPanel(true);
41237         this.footer.dataSource = this.grid.dataSource;
41238         this.footer = Roo.factory(this.footer, Roo);
41239         this.footer.render(ctr);
41240         
41241     }
41242     
41243     
41244     
41245     
41246      
41247 };
41248
41249 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41250     getId : function(){
41251         return this.grid.id;
41252     },
41253     
41254     /**
41255      * Returns the grid for this panel
41256      * @return {Roo.bootstrap.Table} 
41257      */
41258     getGrid : function(){
41259         return this.grid;    
41260     },
41261     
41262     setSize : function(width, height){
41263         if(!this.ignoreResize(width, height)){
41264             var grid = this.grid;
41265             var size = this.adjustForComponents(width, height);
41266             // tfoot is not a footer?
41267           
41268             
41269             var gridel = grid.getGridEl();
41270             gridel.setSize(size.width, size.height);
41271             
41272             var tbd = grid.getGridEl().select('tbody', true).first();
41273             var thd = grid.getGridEl().select('thead',true).first();
41274             var tbf= grid.getGridEl().select('tfoot', true).first();
41275
41276             if (tbf) {
41277                 size.height -= tbf.getHeight();
41278             }
41279             if (thd) {
41280                 size.height -= thd.getHeight();
41281             }
41282             
41283             tbd.setSize(size.width, size.height );
41284             // this is for the account management tab -seems to work there.
41285             var thd = grid.getGridEl().select('thead',true).first();
41286             //if (tbd) {
41287             //    tbd.setSize(size.width, size.height - thd.getHeight());
41288             //}
41289              
41290             grid.autoSize();
41291         }
41292     },
41293      
41294     
41295     
41296     beforeSlide : function(){
41297         this.grid.getView().scroller.clip();
41298     },
41299     
41300     afterSlide : function(){
41301         this.grid.getView().scroller.unclip();
41302     },
41303     
41304     destroy : function(){
41305         this.grid.destroy();
41306         delete this.grid;
41307         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41308     }
41309 });
41310
41311 /**
41312  * @class Roo.bootstrap.panel.Nest
41313  * @extends Roo.bootstrap.panel.Content
41314  * @constructor
41315  * Create a new Panel, that can contain a layout.Border.
41316  * 
41317  * 
41318  * @param {Roo.BorderLayout} layout The layout for this panel
41319  * @param {String/Object} config A string to set only the title or a config object
41320  */
41321 Roo.bootstrap.panel.Nest = function(config)
41322 {
41323     // construct with only one argument..
41324     /* FIXME - implement nicer consturctors
41325     if (layout.layout) {
41326         config = layout;
41327         layout = config.layout;
41328         delete config.layout;
41329     }
41330     if (layout.xtype && !layout.getEl) {
41331         // then layout needs constructing..
41332         layout = Roo.factory(layout, Roo);
41333     }
41334     */
41335     
41336     config.el =  config.layout.getEl();
41337     
41338     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41339     
41340     config.layout.monitorWindowResize = false; // turn off autosizing
41341     this.layout = config.layout;
41342     this.layout.getEl().addClass("roo-layout-nested-layout");
41343     this.layout.parent = this;
41344     
41345     
41346     
41347     
41348 };
41349
41350 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41351
41352     setSize : function(width, height){
41353         if(!this.ignoreResize(width, height)){
41354             var size = this.adjustForComponents(width, height);
41355             var el = this.layout.getEl();
41356             if (size.height < 1) {
41357                 el.setWidth(size.width);   
41358             } else {
41359                 el.setSize(size.width, size.height);
41360             }
41361             var touch = el.dom.offsetWidth;
41362             this.layout.layout();
41363             // ie requires a double layout on the first pass
41364             if(Roo.isIE && !this.initialized){
41365                 this.initialized = true;
41366                 this.layout.layout();
41367             }
41368         }
41369     },
41370     
41371     // activate all subpanels if not currently active..
41372     
41373     setActiveState : function(active){
41374         this.active = active;
41375         this.setActiveClass(active);
41376         
41377         if(!active){
41378             this.fireEvent("deactivate", this);
41379             return;
41380         }
41381         
41382         this.fireEvent("activate", this);
41383         // not sure if this should happen before or after..
41384         if (!this.layout) {
41385             return; // should not happen..
41386         }
41387         var reg = false;
41388         for (var r in this.layout.regions) {
41389             reg = this.layout.getRegion(r);
41390             if (reg.getActivePanel()) {
41391                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41392                 reg.setActivePanel(reg.getActivePanel());
41393                 continue;
41394             }
41395             if (!reg.panels.length) {
41396                 continue;
41397             }
41398             reg.showPanel(reg.getPanel(0));
41399         }
41400         
41401         
41402         
41403         
41404     },
41405     
41406     /**
41407      * Returns the nested BorderLayout for this panel
41408      * @return {Roo.BorderLayout} 
41409      */
41410     getLayout : function(){
41411         return this.layout;
41412     },
41413     
41414      /**
41415      * Adds a xtype elements to the layout of the nested panel
41416      * <pre><code>
41417
41418 panel.addxtype({
41419        xtype : 'ContentPanel',
41420        region: 'west',
41421        items: [ .... ]
41422    }
41423 );
41424
41425 panel.addxtype({
41426         xtype : 'NestedLayoutPanel',
41427         region: 'west',
41428         layout: {
41429            center: { },
41430            west: { }   
41431         },
41432         items : [ ... list of content panels or nested layout panels.. ]
41433    }
41434 );
41435 </code></pre>
41436      * @param {Object} cfg Xtype definition of item to add.
41437      */
41438     addxtype : function(cfg) {
41439         return this.layout.addxtype(cfg);
41440     
41441     }
41442 });/*
41443  * Based on:
41444  * Ext JS Library 1.1.1
41445  * Copyright(c) 2006-2007, Ext JS, LLC.
41446  *
41447  * Originally Released Under LGPL - original licence link has changed is not relivant.
41448  *
41449  * Fork - LGPL
41450  * <script type="text/javascript">
41451  */
41452 /**
41453  * @class Roo.TabPanel
41454  * @extends Roo.util.Observable
41455  * A lightweight tab container.
41456  * <br><br>
41457  * Usage:
41458  * <pre><code>
41459 // basic tabs 1, built from existing content
41460 var tabs = new Roo.TabPanel("tabs1");
41461 tabs.addTab("script", "View Script");
41462 tabs.addTab("markup", "View Markup");
41463 tabs.activate("script");
41464
41465 // more advanced tabs, built from javascript
41466 var jtabs = new Roo.TabPanel("jtabs");
41467 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41468
41469 // set up the UpdateManager
41470 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41471 var updater = tab2.getUpdateManager();
41472 updater.setDefaultUrl("ajax1.htm");
41473 tab2.on('activate', updater.refresh, updater, true);
41474
41475 // Use setUrl for Ajax loading
41476 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41477 tab3.setUrl("ajax2.htm", null, true);
41478
41479 // Disabled tab
41480 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41481 tab4.disable();
41482
41483 jtabs.activate("jtabs-1");
41484  * </code></pre>
41485  * @constructor
41486  * Create a new TabPanel.
41487  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41488  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41489  */
41490 Roo.bootstrap.panel.Tabs = function(config){
41491     /**
41492     * The container element for this TabPanel.
41493     * @type Roo.Element
41494     */
41495     this.el = Roo.get(config.el);
41496     delete config.el;
41497     if(config){
41498         if(typeof config == "boolean"){
41499             this.tabPosition = config ? "bottom" : "top";
41500         }else{
41501             Roo.apply(this, config);
41502         }
41503     }
41504     
41505     if(this.tabPosition == "bottom"){
41506         // if tabs are at the bottom = create the body first.
41507         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41508         this.el.addClass("roo-tabs-bottom");
41509     }
41510     // next create the tabs holders
41511     
41512     if (this.tabPosition == "west"){
41513         
41514         var reg = this.region; // fake it..
41515         while (reg) {
41516             if (!reg.mgr.parent) {
41517                 break;
41518             }
41519             reg = reg.mgr.parent.region;
41520         }
41521         Roo.log("got nest?");
41522         Roo.log(reg);
41523         if (reg.mgr.getRegion('west')) {
41524             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41525             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41526             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41527             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41528             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41529         
41530             
41531         }
41532         
41533         
41534     } else {
41535      
41536         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41537         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41538         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41539         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41540     }
41541     
41542     
41543     if(Roo.isIE){
41544         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41545     }
41546     
41547     // finally - if tabs are at the top, then create the body last..
41548     if(this.tabPosition != "bottom"){
41549         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41550          * @type Roo.Element
41551          */
41552         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41553         this.el.addClass("roo-tabs-top");
41554     }
41555     this.items = [];
41556
41557     this.bodyEl.setStyle("position", "relative");
41558
41559     this.active = null;
41560     this.activateDelegate = this.activate.createDelegate(this);
41561
41562     this.addEvents({
41563         /**
41564          * @event tabchange
41565          * Fires when the active tab changes
41566          * @param {Roo.TabPanel} this
41567          * @param {Roo.TabPanelItem} activePanel The new active tab
41568          */
41569         "tabchange": true,
41570         /**
41571          * @event beforetabchange
41572          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41573          * @param {Roo.TabPanel} this
41574          * @param {Object} e Set cancel to true on this object to cancel the tab change
41575          * @param {Roo.TabPanelItem} tab The tab being changed to
41576          */
41577         "beforetabchange" : true
41578     });
41579
41580     Roo.EventManager.onWindowResize(this.onResize, this);
41581     this.cpad = this.el.getPadding("lr");
41582     this.hiddenCount = 0;
41583
41584
41585     // toolbar on the tabbar support...
41586     if (this.toolbar) {
41587         alert("no toolbar support yet");
41588         this.toolbar  = false;
41589         /*
41590         var tcfg = this.toolbar;
41591         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41592         this.toolbar = new Roo.Toolbar(tcfg);
41593         if (Roo.isSafari) {
41594             var tbl = tcfg.container.child('table', true);
41595             tbl.setAttribute('width', '100%');
41596         }
41597         */
41598         
41599     }
41600    
41601
41602
41603     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41604 };
41605
41606 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41607     /*
41608      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41609      */
41610     tabPosition : "top",
41611     /*
41612      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41613      */
41614     currentTabWidth : 0,
41615     /*
41616      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41617      */
41618     minTabWidth : 40,
41619     /*
41620      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41621      */
41622     maxTabWidth : 250,
41623     /*
41624      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41625      */
41626     preferredTabWidth : 175,
41627     /*
41628      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41629      */
41630     resizeTabs : false,
41631     /*
41632      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41633      */
41634     monitorResize : true,
41635     /*
41636      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41637      */
41638     toolbar : false,  // set by caller..
41639     
41640     region : false, /// set by caller
41641     
41642     disableTooltips : true, // not used yet...
41643
41644     /**
41645      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41646      * @param {String} id The id of the div to use <b>or create</b>
41647      * @param {String} text The text for the tab
41648      * @param {String} content (optional) Content to put in the TabPanelItem body
41649      * @param {Boolean} closable (optional) True to create a close icon on the tab
41650      * @return {Roo.TabPanelItem} The created TabPanelItem
41651      */
41652     addTab : function(id, text, content, closable, tpl)
41653     {
41654         var item = new Roo.bootstrap.panel.TabItem({
41655             panel: this,
41656             id : id,
41657             text : text,
41658             closable : closable,
41659             tpl : tpl
41660         });
41661         this.addTabItem(item);
41662         if(content){
41663             item.setContent(content);
41664         }
41665         return item;
41666     },
41667
41668     /**
41669      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41670      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41671      * @return {Roo.TabPanelItem}
41672      */
41673     getTab : function(id){
41674         return this.items[id];
41675     },
41676
41677     /**
41678      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41679      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41680      */
41681     hideTab : function(id){
41682         var t = this.items[id];
41683         if(!t.isHidden()){
41684            t.setHidden(true);
41685            this.hiddenCount++;
41686            this.autoSizeTabs();
41687         }
41688     },
41689
41690     /**
41691      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41692      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41693      */
41694     unhideTab : function(id){
41695         var t = this.items[id];
41696         if(t.isHidden()){
41697            t.setHidden(false);
41698            this.hiddenCount--;
41699            this.autoSizeTabs();
41700         }
41701     },
41702
41703     /**
41704      * Adds an existing {@link Roo.TabPanelItem}.
41705      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41706      */
41707     addTabItem : function(item)
41708     {
41709         this.items[item.id] = item;
41710         this.items.push(item);
41711         this.autoSizeTabs();
41712       //  if(this.resizeTabs){
41713     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41714   //         this.autoSizeTabs();
41715 //        }else{
41716 //            item.autoSize();
41717        // }
41718     },
41719
41720     /**
41721      * Removes a {@link Roo.TabPanelItem}.
41722      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41723      */
41724     removeTab : function(id){
41725         var items = this.items;
41726         var tab = items[id];
41727         if(!tab) { return; }
41728         var index = items.indexOf(tab);
41729         if(this.active == tab && items.length > 1){
41730             var newTab = this.getNextAvailable(index);
41731             if(newTab) {
41732                 newTab.activate();
41733             }
41734         }
41735         this.stripEl.dom.removeChild(tab.pnode.dom);
41736         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41737             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41738         }
41739         items.splice(index, 1);
41740         delete this.items[tab.id];
41741         tab.fireEvent("close", tab);
41742         tab.purgeListeners();
41743         this.autoSizeTabs();
41744     },
41745
41746     getNextAvailable : function(start){
41747         var items = this.items;
41748         var index = start;
41749         // look for a next tab that will slide over to
41750         // replace the one being removed
41751         while(index < items.length){
41752             var item = items[++index];
41753             if(item && !item.isHidden()){
41754                 return item;
41755             }
41756         }
41757         // if one isn't found select the previous tab (on the left)
41758         index = start;
41759         while(index >= 0){
41760             var item = items[--index];
41761             if(item && !item.isHidden()){
41762                 return item;
41763             }
41764         }
41765         return null;
41766     },
41767
41768     /**
41769      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41770      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41771      */
41772     disableTab : function(id){
41773         var tab = this.items[id];
41774         if(tab && this.active != tab){
41775             tab.disable();
41776         }
41777     },
41778
41779     /**
41780      * Enables a {@link Roo.TabPanelItem} that is disabled.
41781      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41782      */
41783     enableTab : function(id){
41784         var tab = this.items[id];
41785         tab.enable();
41786     },
41787
41788     /**
41789      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41790      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41791      * @return {Roo.TabPanelItem} The TabPanelItem.
41792      */
41793     activate : function(id)
41794     {
41795         //Roo.log('activite:'  + id);
41796         
41797         var tab = this.items[id];
41798         if(!tab){
41799             return null;
41800         }
41801         if(tab == this.active || tab.disabled){
41802             return tab;
41803         }
41804         var e = {};
41805         this.fireEvent("beforetabchange", this, e, tab);
41806         if(e.cancel !== true && !tab.disabled){
41807             if(this.active){
41808                 this.active.hide();
41809             }
41810             this.active = this.items[id];
41811             this.active.show();
41812             this.fireEvent("tabchange", this, this.active);
41813         }
41814         return tab;
41815     },
41816
41817     /**
41818      * Gets the active {@link Roo.TabPanelItem}.
41819      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41820      */
41821     getActiveTab : function(){
41822         return this.active;
41823     },
41824
41825     /**
41826      * Updates the tab body element to fit the height of the container element
41827      * for overflow scrolling
41828      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41829      */
41830     syncHeight : function(targetHeight){
41831         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41832         var bm = this.bodyEl.getMargins();
41833         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41834         this.bodyEl.setHeight(newHeight);
41835         return newHeight;
41836     },
41837
41838     onResize : function(){
41839         if(this.monitorResize){
41840             this.autoSizeTabs();
41841         }
41842     },
41843
41844     /**
41845      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41846      */
41847     beginUpdate : function(){
41848         this.updating = true;
41849     },
41850
41851     /**
41852      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41853      */
41854     endUpdate : function(){
41855         this.updating = false;
41856         this.autoSizeTabs();
41857     },
41858
41859     /**
41860      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41861      */
41862     autoSizeTabs : function()
41863     {
41864         var count = this.items.length;
41865         var vcount = count - this.hiddenCount;
41866         
41867         if (vcount < 2) {
41868             this.stripEl.hide();
41869         } else {
41870             this.stripEl.show();
41871         }
41872         
41873         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41874             return;
41875         }
41876         
41877         
41878         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41879         var availWidth = Math.floor(w / vcount);
41880         var b = this.stripBody;
41881         if(b.getWidth() > w){
41882             var tabs = this.items;
41883             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41884             if(availWidth < this.minTabWidth){
41885                 /*if(!this.sleft){    // incomplete scrolling code
41886                     this.createScrollButtons();
41887                 }
41888                 this.showScroll();
41889                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41890             }
41891         }else{
41892             if(this.currentTabWidth < this.preferredTabWidth){
41893                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41894             }
41895         }
41896     },
41897
41898     /**
41899      * Returns the number of tabs in this TabPanel.
41900      * @return {Number}
41901      */
41902      getCount : function(){
41903          return this.items.length;
41904      },
41905
41906     /**
41907      * Resizes all the tabs to the passed width
41908      * @param {Number} The new width
41909      */
41910     setTabWidth : function(width){
41911         this.currentTabWidth = width;
41912         for(var i = 0, len = this.items.length; i < len; i++) {
41913                 if(!this.items[i].isHidden()) {
41914                 this.items[i].setWidth(width);
41915             }
41916         }
41917     },
41918
41919     /**
41920      * Destroys this TabPanel
41921      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41922      */
41923     destroy : function(removeEl){
41924         Roo.EventManager.removeResizeListener(this.onResize, this);
41925         for(var i = 0, len = this.items.length; i < len; i++){
41926             this.items[i].purgeListeners();
41927         }
41928         if(removeEl === true){
41929             this.el.update("");
41930             this.el.remove();
41931         }
41932     },
41933     
41934     createStrip : function(container)
41935     {
41936         var strip = document.createElement("nav");
41937         strip.className = Roo.bootstrap.version == 4 ?
41938             "navbar-light bg-light" : 
41939             "navbar navbar-default"; //"x-tabs-wrap";
41940         container.appendChild(strip);
41941         return strip;
41942     },
41943     
41944     createStripList : function(strip)
41945     {
41946         // div wrapper for retard IE
41947         // returns the "tr" element.
41948         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41949         //'<div class="x-tabs-strip-wrap">'+
41950           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41951           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41952         return strip.firstChild; //.firstChild.firstChild.firstChild;
41953     },
41954     createBody : function(container)
41955     {
41956         var body = document.createElement("div");
41957         Roo.id(body, "tab-body");
41958         //Roo.fly(body).addClass("x-tabs-body");
41959         Roo.fly(body).addClass("tab-content");
41960         container.appendChild(body);
41961         return body;
41962     },
41963     createItemBody :function(bodyEl, id){
41964         var body = Roo.getDom(id);
41965         if(!body){
41966             body = document.createElement("div");
41967             body.id = id;
41968         }
41969         //Roo.fly(body).addClass("x-tabs-item-body");
41970         Roo.fly(body).addClass("tab-pane");
41971          bodyEl.insertBefore(body, bodyEl.firstChild);
41972         return body;
41973     },
41974     /** @private */
41975     createStripElements :  function(stripEl, text, closable, tpl)
41976     {
41977         var td = document.createElement("li"); // was td..
41978         td.className = 'nav-item';
41979         
41980         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41981         
41982         
41983         stripEl.appendChild(td);
41984         /*if(closable){
41985             td.className = "x-tabs-closable";
41986             if(!this.closeTpl){
41987                 this.closeTpl = new Roo.Template(
41988                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41989                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41990                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41991                 );
41992             }
41993             var el = this.closeTpl.overwrite(td, {"text": text});
41994             var close = el.getElementsByTagName("div")[0];
41995             var inner = el.getElementsByTagName("em")[0];
41996             return {"el": el, "close": close, "inner": inner};
41997         } else {
41998         */
41999         // not sure what this is..
42000 //            if(!this.tabTpl){
42001                 //this.tabTpl = new Roo.Template(
42002                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42003                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42004                 //);
42005 //                this.tabTpl = new Roo.Template(
42006 //                   '<a href="#">' +
42007 //                   '<span unselectable="on"' +
42008 //                            (this.disableTooltips ? '' : ' title="{text}"') +
42009 //                            ' >{text}</span></a>'
42010 //                );
42011 //                
42012 //            }
42013
42014
42015             var template = tpl || this.tabTpl || false;
42016             
42017             if(!template){
42018                 template =  new Roo.Template(
42019                         Roo.bootstrap.version == 4 ? 
42020                             (
42021                                 '<a class="nav-link" href="#" unselectable="on"' +
42022                                      (this.disableTooltips ? '' : ' title="{text}"') +
42023                                      ' >{text}</a>'
42024                             ) : (
42025                                 '<a class="nav-link" href="#">' +
42026                                 '<span unselectable="on"' +
42027                                          (this.disableTooltips ? '' : ' title="{text}"') +
42028                                     ' >{text}</span></a>'
42029                             )
42030                 );
42031             }
42032             
42033             switch (typeof(template)) {
42034                 case 'object' :
42035                     break;
42036                 case 'string' :
42037                     template = new Roo.Template(template);
42038                     break;
42039                 default :
42040                     break;
42041             }
42042             
42043             var el = template.overwrite(td, {"text": text});
42044             
42045             var inner = el.getElementsByTagName("span")[0];
42046             
42047             return {"el": el, "inner": inner};
42048             
42049     }
42050         
42051     
42052 });
42053
42054 /**
42055  * @class Roo.TabPanelItem
42056  * @extends Roo.util.Observable
42057  * Represents an individual item (tab plus body) in a TabPanel.
42058  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42059  * @param {String} id The id of this TabPanelItem
42060  * @param {String} text The text for the tab of this TabPanelItem
42061  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42062  */
42063 Roo.bootstrap.panel.TabItem = function(config){
42064     /**
42065      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42066      * @type Roo.TabPanel
42067      */
42068     this.tabPanel = config.panel;
42069     /**
42070      * The id for this TabPanelItem
42071      * @type String
42072      */
42073     this.id = config.id;
42074     /** @private */
42075     this.disabled = false;
42076     /** @private */
42077     this.text = config.text;
42078     /** @private */
42079     this.loaded = false;
42080     this.closable = config.closable;
42081
42082     /**
42083      * The body element for this TabPanelItem.
42084      * @type Roo.Element
42085      */
42086     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42087     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42088     this.bodyEl.setStyle("display", "block");
42089     this.bodyEl.setStyle("zoom", "1");
42090     //this.hideAction();
42091
42092     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42093     /** @private */
42094     this.el = Roo.get(els.el);
42095     this.inner = Roo.get(els.inner, true);
42096      this.textEl = Roo.bootstrap.version == 4 ?
42097         this.el : Roo.get(this.el.dom.firstChild, true);
42098
42099     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42100     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42101
42102     
42103 //    this.el.on("mousedown", this.onTabMouseDown, this);
42104     this.el.on("click", this.onTabClick, this);
42105     /** @private */
42106     if(config.closable){
42107         var c = Roo.get(els.close, true);
42108         c.dom.title = this.closeText;
42109         c.addClassOnOver("close-over");
42110         c.on("click", this.closeClick, this);
42111      }
42112
42113     this.addEvents({
42114          /**
42115          * @event activate
42116          * Fires when this tab becomes the active tab.
42117          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42118          * @param {Roo.TabPanelItem} this
42119          */
42120         "activate": true,
42121         /**
42122          * @event beforeclose
42123          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42124          * @param {Roo.TabPanelItem} this
42125          * @param {Object} e Set cancel to true on this object to cancel the close.
42126          */
42127         "beforeclose": true,
42128         /**
42129          * @event close
42130          * Fires when this tab is closed.
42131          * @param {Roo.TabPanelItem} this
42132          */
42133          "close": true,
42134         /**
42135          * @event deactivate
42136          * Fires when this tab is no longer the active tab.
42137          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42138          * @param {Roo.TabPanelItem} this
42139          */
42140          "deactivate" : true
42141     });
42142     this.hidden = false;
42143
42144     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42145 };
42146
42147 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42148            {
42149     purgeListeners : function(){
42150        Roo.util.Observable.prototype.purgeListeners.call(this);
42151        this.el.removeAllListeners();
42152     },
42153     /**
42154      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42155      */
42156     show : function(){
42157         this.status_node.addClass("active");
42158         this.showAction();
42159         if(Roo.isOpera){
42160             this.tabPanel.stripWrap.repaint();
42161         }
42162         this.fireEvent("activate", this.tabPanel, this);
42163     },
42164
42165     /**
42166      * Returns true if this tab is the active tab.
42167      * @return {Boolean}
42168      */
42169     isActive : function(){
42170         return this.tabPanel.getActiveTab() == this;
42171     },
42172
42173     /**
42174      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42175      */
42176     hide : function(){
42177         this.status_node.removeClass("active");
42178         this.hideAction();
42179         this.fireEvent("deactivate", this.tabPanel, this);
42180     },
42181
42182     hideAction : function(){
42183         this.bodyEl.hide();
42184         this.bodyEl.setStyle("position", "absolute");
42185         this.bodyEl.setLeft("-20000px");
42186         this.bodyEl.setTop("-20000px");
42187     },
42188
42189     showAction : function(){
42190         this.bodyEl.setStyle("position", "relative");
42191         this.bodyEl.setTop("");
42192         this.bodyEl.setLeft("");
42193         this.bodyEl.show();
42194     },
42195
42196     /**
42197      * Set the tooltip for the tab.
42198      * @param {String} tooltip The tab's tooltip
42199      */
42200     setTooltip : function(text){
42201         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42202             this.textEl.dom.qtip = text;
42203             this.textEl.dom.removeAttribute('title');
42204         }else{
42205             this.textEl.dom.title = text;
42206         }
42207     },
42208
42209     onTabClick : function(e){
42210         e.preventDefault();
42211         this.tabPanel.activate(this.id);
42212     },
42213
42214     onTabMouseDown : function(e){
42215         e.preventDefault();
42216         this.tabPanel.activate(this.id);
42217     },
42218 /*
42219     getWidth : function(){
42220         return this.inner.getWidth();
42221     },
42222
42223     setWidth : function(width){
42224         var iwidth = width - this.linode.getPadding("lr");
42225         this.inner.setWidth(iwidth);
42226         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42227         this.linode.setWidth(width);
42228     },
42229 */
42230     /**
42231      * Show or hide the tab
42232      * @param {Boolean} hidden True to hide or false to show.
42233      */
42234     setHidden : function(hidden){
42235         this.hidden = hidden;
42236         this.linode.setStyle("display", hidden ? "none" : "");
42237     },
42238
42239     /**
42240      * Returns true if this tab is "hidden"
42241      * @return {Boolean}
42242      */
42243     isHidden : function(){
42244         return this.hidden;
42245     },
42246
42247     /**
42248      * Returns the text for this tab
42249      * @return {String}
42250      */
42251     getText : function(){
42252         return this.text;
42253     },
42254     /*
42255     autoSize : function(){
42256         //this.el.beginMeasure();
42257         this.textEl.setWidth(1);
42258         /*
42259          *  #2804 [new] Tabs in Roojs
42260          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42261          */
42262         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42263         //this.el.endMeasure();
42264     //},
42265
42266     /**
42267      * Sets the text for the tab (Note: this also sets the tooltip text)
42268      * @param {String} text The tab's text and tooltip
42269      */
42270     setText : function(text){
42271         this.text = text;
42272         this.textEl.update(text);
42273         this.setTooltip(text);
42274         //if(!this.tabPanel.resizeTabs){
42275         //    this.autoSize();
42276         //}
42277     },
42278     /**
42279      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42280      */
42281     activate : function(){
42282         this.tabPanel.activate(this.id);
42283     },
42284
42285     /**
42286      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42287      */
42288     disable : function(){
42289         if(this.tabPanel.active != this){
42290             this.disabled = true;
42291             this.status_node.addClass("disabled");
42292         }
42293     },
42294
42295     /**
42296      * Enables this TabPanelItem if it was previously disabled.
42297      */
42298     enable : function(){
42299         this.disabled = false;
42300         this.status_node.removeClass("disabled");
42301     },
42302
42303     /**
42304      * Sets the content for this TabPanelItem.
42305      * @param {String} content The content
42306      * @param {Boolean} loadScripts true to look for and load scripts
42307      */
42308     setContent : function(content, loadScripts){
42309         this.bodyEl.update(content, loadScripts);
42310     },
42311
42312     /**
42313      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42314      * @return {Roo.UpdateManager} The UpdateManager
42315      */
42316     getUpdateManager : function(){
42317         return this.bodyEl.getUpdateManager();
42318     },
42319
42320     /**
42321      * Set a URL to be used to load the content for this TabPanelItem.
42322      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42323      * @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)
42324      * @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)
42325      * @return {Roo.UpdateManager} The UpdateManager
42326      */
42327     setUrl : function(url, params, loadOnce){
42328         if(this.refreshDelegate){
42329             this.un('activate', this.refreshDelegate);
42330         }
42331         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42332         this.on("activate", this.refreshDelegate);
42333         return this.bodyEl.getUpdateManager();
42334     },
42335
42336     /** @private */
42337     _handleRefresh : function(url, params, loadOnce){
42338         if(!loadOnce || !this.loaded){
42339             var updater = this.bodyEl.getUpdateManager();
42340             updater.update(url, params, this._setLoaded.createDelegate(this));
42341         }
42342     },
42343
42344     /**
42345      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42346      *   Will fail silently if the setUrl method has not been called.
42347      *   This does not activate the panel, just updates its content.
42348      */
42349     refresh : function(){
42350         if(this.refreshDelegate){
42351            this.loaded = false;
42352            this.refreshDelegate();
42353         }
42354     },
42355
42356     /** @private */
42357     _setLoaded : function(){
42358         this.loaded = true;
42359     },
42360
42361     /** @private */
42362     closeClick : function(e){
42363         var o = {};
42364         e.stopEvent();
42365         this.fireEvent("beforeclose", this, o);
42366         if(o.cancel !== true){
42367             this.tabPanel.removeTab(this.id);
42368         }
42369     },
42370     /**
42371      * The text displayed in the tooltip for the close icon.
42372      * @type String
42373      */
42374     closeText : "Close this tab"
42375 });
42376 /**
42377 *    This script refer to:
42378 *    Title: International Telephone Input
42379 *    Author: Jack O'Connor
42380 *    Code version:  v12.1.12
42381 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42382 **/
42383
42384 Roo.bootstrap.PhoneInputData = function() {
42385     var d = [
42386       [
42387         "Afghanistan (‫افغانستان‬‎)",
42388         "af",
42389         "93"
42390       ],
42391       [
42392         "Albania (Shqipëri)",
42393         "al",
42394         "355"
42395       ],
42396       [
42397         "Algeria (‫الجزائر‬‎)",
42398         "dz",
42399         "213"
42400       ],
42401       [
42402         "American Samoa",
42403         "as",
42404         "1684"
42405       ],
42406       [
42407         "Andorra",
42408         "ad",
42409         "376"
42410       ],
42411       [
42412         "Angola",
42413         "ao",
42414         "244"
42415       ],
42416       [
42417         "Anguilla",
42418         "ai",
42419         "1264"
42420       ],
42421       [
42422         "Antigua and Barbuda",
42423         "ag",
42424         "1268"
42425       ],
42426       [
42427         "Argentina",
42428         "ar",
42429         "54"
42430       ],
42431       [
42432         "Armenia (Հայաստան)",
42433         "am",
42434         "374"
42435       ],
42436       [
42437         "Aruba",
42438         "aw",
42439         "297"
42440       ],
42441       [
42442         "Australia",
42443         "au",
42444         "61",
42445         0
42446       ],
42447       [
42448         "Austria (Österreich)",
42449         "at",
42450         "43"
42451       ],
42452       [
42453         "Azerbaijan (Azərbaycan)",
42454         "az",
42455         "994"
42456       ],
42457       [
42458         "Bahamas",
42459         "bs",
42460         "1242"
42461       ],
42462       [
42463         "Bahrain (‫البحرين‬‎)",
42464         "bh",
42465         "973"
42466       ],
42467       [
42468         "Bangladesh (বাংলাদেশ)",
42469         "bd",
42470         "880"
42471       ],
42472       [
42473         "Barbados",
42474         "bb",
42475         "1246"
42476       ],
42477       [
42478         "Belarus (Беларусь)",
42479         "by",
42480         "375"
42481       ],
42482       [
42483         "Belgium (België)",
42484         "be",
42485         "32"
42486       ],
42487       [
42488         "Belize",
42489         "bz",
42490         "501"
42491       ],
42492       [
42493         "Benin (Bénin)",
42494         "bj",
42495         "229"
42496       ],
42497       [
42498         "Bermuda",
42499         "bm",
42500         "1441"
42501       ],
42502       [
42503         "Bhutan (འབྲུག)",
42504         "bt",
42505         "975"
42506       ],
42507       [
42508         "Bolivia",
42509         "bo",
42510         "591"
42511       ],
42512       [
42513         "Bosnia and Herzegovina (Босна и Херцеговина)",
42514         "ba",
42515         "387"
42516       ],
42517       [
42518         "Botswana",
42519         "bw",
42520         "267"
42521       ],
42522       [
42523         "Brazil (Brasil)",
42524         "br",
42525         "55"
42526       ],
42527       [
42528         "British Indian Ocean Territory",
42529         "io",
42530         "246"
42531       ],
42532       [
42533         "British Virgin Islands",
42534         "vg",
42535         "1284"
42536       ],
42537       [
42538         "Brunei",
42539         "bn",
42540         "673"
42541       ],
42542       [
42543         "Bulgaria (България)",
42544         "bg",
42545         "359"
42546       ],
42547       [
42548         "Burkina Faso",
42549         "bf",
42550         "226"
42551       ],
42552       [
42553         "Burundi (Uburundi)",
42554         "bi",
42555         "257"
42556       ],
42557       [
42558         "Cambodia (កម្ពុជា)",
42559         "kh",
42560         "855"
42561       ],
42562       [
42563         "Cameroon (Cameroun)",
42564         "cm",
42565         "237"
42566       ],
42567       [
42568         "Canada",
42569         "ca",
42570         "1",
42571         1,
42572         ["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"]
42573       ],
42574       [
42575         "Cape Verde (Kabu Verdi)",
42576         "cv",
42577         "238"
42578       ],
42579       [
42580         "Caribbean Netherlands",
42581         "bq",
42582         "599",
42583         1
42584       ],
42585       [
42586         "Cayman Islands",
42587         "ky",
42588         "1345"
42589       ],
42590       [
42591         "Central African Republic (République centrafricaine)",
42592         "cf",
42593         "236"
42594       ],
42595       [
42596         "Chad (Tchad)",
42597         "td",
42598         "235"
42599       ],
42600       [
42601         "Chile",
42602         "cl",
42603         "56"
42604       ],
42605       [
42606         "China (中国)",
42607         "cn",
42608         "86"
42609       ],
42610       [
42611         "Christmas Island",
42612         "cx",
42613         "61",
42614         2
42615       ],
42616       [
42617         "Cocos (Keeling) Islands",
42618         "cc",
42619         "61",
42620         1
42621       ],
42622       [
42623         "Colombia",
42624         "co",
42625         "57"
42626       ],
42627       [
42628         "Comoros (‫جزر القمر‬‎)",
42629         "km",
42630         "269"
42631       ],
42632       [
42633         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42634         "cd",
42635         "243"
42636       ],
42637       [
42638         "Congo (Republic) (Congo-Brazzaville)",
42639         "cg",
42640         "242"
42641       ],
42642       [
42643         "Cook Islands",
42644         "ck",
42645         "682"
42646       ],
42647       [
42648         "Costa Rica",
42649         "cr",
42650         "506"
42651       ],
42652       [
42653         "Côte d’Ivoire",
42654         "ci",
42655         "225"
42656       ],
42657       [
42658         "Croatia (Hrvatska)",
42659         "hr",
42660         "385"
42661       ],
42662       [
42663         "Cuba",
42664         "cu",
42665         "53"
42666       ],
42667       [
42668         "Curaçao",
42669         "cw",
42670         "599",
42671         0
42672       ],
42673       [
42674         "Cyprus (Κύπρος)",
42675         "cy",
42676         "357"
42677       ],
42678       [
42679         "Czech Republic (Česká republika)",
42680         "cz",
42681         "420"
42682       ],
42683       [
42684         "Denmark (Danmark)",
42685         "dk",
42686         "45"
42687       ],
42688       [
42689         "Djibouti",
42690         "dj",
42691         "253"
42692       ],
42693       [
42694         "Dominica",
42695         "dm",
42696         "1767"
42697       ],
42698       [
42699         "Dominican Republic (República Dominicana)",
42700         "do",
42701         "1",
42702         2,
42703         ["809", "829", "849"]
42704       ],
42705       [
42706         "Ecuador",
42707         "ec",
42708         "593"
42709       ],
42710       [
42711         "Egypt (‫مصر‬‎)",
42712         "eg",
42713         "20"
42714       ],
42715       [
42716         "El Salvador",
42717         "sv",
42718         "503"
42719       ],
42720       [
42721         "Equatorial Guinea (Guinea Ecuatorial)",
42722         "gq",
42723         "240"
42724       ],
42725       [
42726         "Eritrea",
42727         "er",
42728         "291"
42729       ],
42730       [
42731         "Estonia (Eesti)",
42732         "ee",
42733         "372"
42734       ],
42735       [
42736         "Ethiopia",
42737         "et",
42738         "251"
42739       ],
42740       [
42741         "Falkland Islands (Islas Malvinas)",
42742         "fk",
42743         "500"
42744       ],
42745       [
42746         "Faroe Islands (Føroyar)",
42747         "fo",
42748         "298"
42749       ],
42750       [
42751         "Fiji",
42752         "fj",
42753         "679"
42754       ],
42755       [
42756         "Finland (Suomi)",
42757         "fi",
42758         "358",
42759         0
42760       ],
42761       [
42762         "France",
42763         "fr",
42764         "33"
42765       ],
42766       [
42767         "French Guiana (Guyane française)",
42768         "gf",
42769         "594"
42770       ],
42771       [
42772         "French Polynesia (Polynésie française)",
42773         "pf",
42774         "689"
42775       ],
42776       [
42777         "Gabon",
42778         "ga",
42779         "241"
42780       ],
42781       [
42782         "Gambia",
42783         "gm",
42784         "220"
42785       ],
42786       [
42787         "Georgia (საქართველო)",
42788         "ge",
42789         "995"
42790       ],
42791       [
42792         "Germany (Deutschland)",
42793         "de",
42794         "49"
42795       ],
42796       [
42797         "Ghana (Gaana)",
42798         "gh",
42799         "233"
42800       ],
42801       [
42802         "Gibraltar",
42803         "gi",
42804         "350"
42805       ],
42806       [
42807         "Greece (Ελλάδα)",
42808         "gr",
42809         "30"
42810       ],
42811       [
42812         "Greenland (Kalaallit Nunaat)",
42813         "gl",
42814         "299"
42815       ],
42816       [
42817         "Grenada",
42818         "gd",
42819         "1473"
42820       ],
42821       [
42822         "Guadeloupe",
42823         "gp",
42824         "590",
42825         0
42826       ],
42827       [
42828         "Guam",
42829         "gu",
42830         "1671"
42831       ],
42832       [
42833         "Guatemala",
42834         "gt",
42835         "502"
42836       ],
42837       [
42838         "Guernsey",
42839         "gg",
42840         "44",
42841         1
42842       ],
42843       [
42844         "Guinea (Guinée)",
42845         "gn",
42846         "224"
42847       ],
42848       [
42849         "Guinea-Bissau (Guiné Bissau)",
42850         "gw",
42851         "245"
42852       ],
42853       [
42854         "Guyana",
42855         "gy",
42856         "592"
42857       ],
42858       [
42859         "Haiti",
42860         "ht",
42861         "509"
42862       ],
42863       [
42864         "Honduras",
42865         "hn",
42866         "504"
42867       ],
42868       [
42869         "Hong Kong (香港)",
42870         "hk",
42871         "852"
42872       ],
42873       [
42874         "Hungary (Magyarország)",
42875         "hu",
42876         "36"
42877       ],
42878       [
42879         "Iceland (Ísland)",
42880         "is",
42881         "354"
42882       ],
42883       [
42884         "India (भारत)",
42885         "in",
42886         "91"
42887       ],
42888       [
42889         "Indonesia",
42890         "id",
42891         "62"
42892       ],
42893       [
42894         "Iran (‫ایران‬‎)",
42895         "ir",
42896         "98"
42897       ],
42898       [
42899         "Iraq (‫العراق‬‎)",
42900         "iq",
42901         "964"
42902       ],
42903       [
42904         "Ireland",
42905         "ie",
42906         "353"
42907       ],
42908       [
42909         "Isle of Man",
42910         "im",
42911         "44",
42912         2
42913       ],
42914       [
42915         "Israel (‫ישראל‬‎)",
42916         "il",
42917         "972"
42918       ],
42919       [
42920         "Italy (Italia)",
42921         "it",
42922         "39",
42923         0
42924       ],
42925       [
42926         "Jamaica",
42927         "jm",
42928         "1876"
42929       ],
42930       [
42931         "Japan (日本)",
42932         "jp",
42933         "81"
42934       ],
42935       [
42936         "Jersey",
42937         "je",
42938         "44",
42939         3
42940       ],
42941       [
42942         "Jordan (‫الأردن‬‎)",
42943         "jo",
42944         "962"
42945       ],
42946       [
42947         "Kazakhstan (Казахстан)",
42948         "kz",
42949         "7",
42950         1
42951       ],
42952       [
42953         "Kenya",
42954         "ke",
42955         "254"
42956       ],
42957       [
42958         "Kiribati",
42959         "ki",
42960         "686"
42961       ],
42962       [
42963         "Kosovo",
42964         "xk",
42965         "383"
42966       ],
42967       [
42968         "Kuwait (‫الكويت‬‎)",
42969         "kw",
42970         "965"
42971       ],
42972       [
42973         "Kyrgyzstan (Кыргызстан)",
42974         "kg",
42975         "996"
42976       ],
42977       [
42978         "Laos (ລາວ)",
42979         "la",
42980         "856"
42981       ],
42982       [
42983         "Latvia (Latvija)",
42984         "lv",
42985         "371"
42986       ],
42987       [
42988         "Lebanon (‫لبنان‬‎)",
42989         "lb",
42990         "961"
42991       ],
42992       [
42993         "Lesotho",
42994         "ls",
42995         "266"
42996       ],
42997       [
42998         "Liberia",
42999         "lr",
43000         "231"
43001       ],
43002       [
43003         "Libya (‫ليبيا‬‎)",
43004         "ly",
43005         "218"
43006       ],
43007       [
43008         "Liechtenstein",
43009         "li",
43010         "423"
43011       ],
43012       [
43013         "Lithuania (Lietuva)",
43014         "lt",
43015         "370"
43016       ],
43017       [
43018         "Luxembourg",
43019         "lu",
43020         "352"
43021       ],
43022       [
43023         "Macau (澳門)",
43024         "mo",
43025         "853"
43026       ],
43027       [
43028         "Macedonia (FYROM) (Македонија)",
43029         "mk",
43030         "389"
43031       ],
43032       [
43033         "Madagascar (Madagasikara)",
43034         "mg",
43035         "261"
43036       ],
43037       [
43038         "Malawi",
43039         "mw",
43040         "265"
43041       ],
43042       [
43043         "Malaysia",
43044         "my",
43045         "60"
43046       ],
43047       [
43048         "Maldives",
43049         "mv",
43050         "960"
43051       ],
43052       [
43053         "Mali",
43054         "ml",
43055         "223"
43056       ],
43057       [
43058         "Malta",
43059         "mt",
43060         "356"
43061       ],
43062       [
43063         "Marshall Islands",
43064         "mh",
43065         "692"
43066       ],
43067       [
43068         "Martinique",
43069         "mq",
43070         "596"
43071       ],
43072       [
43073         "Mauritania (‫موريتانيا‬‎)",
43074         "mr",
43075         "222"
43076       ],
43077       [
43078         "Mauritius (Moris)",
43079         "mu",
43080         "230"
43081       ],
43082       [
43083         "Mayotte",
43084         "yt",
43085         "262",
43086         1
43087       ],
43088       [
43089         "Mexico (México)",
43090         "mx",
43091         "52"
43092       ],
43093       [
43094         "Micronesia",
43095         "fm",
43096         "691"
43097       ],
43098       [
43099         "Moldova (Republica Moldova)",
43100         "md",
43101         "373"
43102       ],
43103       [
43104         "Monaco",
43105         "mc",
43106         "377"
43107       ],
43108       [
43109         "Mongolia (Монгол)",
43110         "mn",
43111         "976"
43112       ],
43113       [
43114         "Montenegro (Crna Gora)",
43115         "me",
43116         "382"
43117       ],
43118       [
43119         "Montserrat",
43120         "ms",
43121         "1664"
43122       ],
43123       [
43124         "Morocco (‫المغرب‬‎)",
43125         "ma",
43126         "212",
43127         0
43128       ],
43129       [
43130         "Mozambique (Moçambique)",
43131         "mz",
43132         "258"
43133       ],
43134       [
43135         "Myanmar (Burma) (မြန်မာ)",
43136         "mm",
43137         "95"
43138       ],
43139       [
43140         "Namibia (Namibië)",
43141         "na",
43142         "264"
43143       ],
43144       [
43145         "Nauru",
43146         "nr",
43147         "674"
43148       ],
43149       [
43150         "Nepal (नेपाल)",
43151         "np",
43152         "977"
43153       ],
43154       [
43155         "Netherlands (Nederland)",
43156         "nl",
43157         "31"
43158       ],
43159       [
43160         "New Caledonia (Nouvelle-Calédonie)",
43161         "nc",
43162         "687"
43163       ],
43164       [
43165         "New Zealand",
43166         "nz",
43167         "64"
43168       ],
43169       [
43170         "Nicaragua",
43171         "ni",
43172         "505"
43173       ],
43174       [
43175         "Niger (Nijar)",
43176         "ne",
43177         "227"
43178       ],
43179       [
43180         "Nigeria",
43181         "ng",
43182         "234"
43183       ],
43184       [
43185         "Niue",
43186         "nu",
43187         "683"
43188       ],
43189       [
43190         "Norfolk Island",
43191         "nf",
43192         "672"
43193       ],
43194       [
43195         "North Korea (조선 민주주의 인민 공화국)",
43196         "kp",
43197         "850"
43198       ],
43199       [
43200         "Northern Mariana Islands",
43201         "mp",
43202         "1670"
43203       ],
43204       [
43205         "Norway (Norge)",
43206         "no",
43207         "47",
43208         0
43209       ],
43210       [
43211         "Oman (‫عُمان‬‎)",
43212         "om",
43213         "968"
43214       ],
43215       [
43216         "Pakistan (‫پاکستان‬‎)",
43217         "pk",
43218         "92"
43219       ],
43220       [
43221         "Palau",
43222         "pw",
43223         "680"
43224       ],
43225       [
43226         "Palestine (‫فلسطين‬‎)",
43227         "ps",
43228         "970"
43229       ],
43230       [
43231         "Panama (Panamá)",
43232         "pa",
43233         "507"
43234       ],
43235       [
43236         "Papua New Guinea",
43237         "pg",
43238         "675"
43239       ],
43240       [
43241         "Paraguay",
43242         "py",
43243         "595"
43244       ],
43245       [
43246         "Peru (Perú)",
43247         "pe",
43248         "51"
43249       ],
43250       [
43251         "Philippines",
43252         "ph",
43253         "63"
43254       ],
43255       [
43256         "Poland (Polska)",
43257         "pl",
43258         "48"
43259       ],
43260       [
43261         "Portugal",
43262         "pt",
43263         "351"
43264       ],
43265       [
43266         "Puerto Rico",
43267         "pr",
43268         "1",
43269         3,
43270         ["787", "939"]
43271       ],
43272       [
43273         "Qatar (‫قطر‬‎)",
43274         "qa",
43275         "974"
43276       ],
43277       [
43278         "Réunion (La Réunion)",
43279         "re",
43280         "262",
43281         0
43282       ],
43283       [
43284         "Romania (România)",
43285         "ro",
43286         "40"
43287       ],
43288       [
43289         "Russia (Россия)",
43290         "ru",
43291         "7",
43292         0
43293       ],
43294       [
43295         "Rwanda",
43296         "rw",
43297         "250"
43298       ],
43299       [
43300         "Saint Barthélemy",
43301         "bl",
43302         "590",
43303         1
43304       ],
43305       [
43306         "Saint Helena",
43307         "sh",
43308         "290"
43309       ],
43310       [
43311         "Saint Kitts and Nevis",
43312         "kn",
43313         "1869"
43314       ],
43315       [
43316         "Saint Lucia",
43317         "lc",
43318         "1758"
43319       ],
43320       [
43321         "Saint Martin (Saint-Martin (partie française))",
43322         "mf",
43323         "590",
43324         2
43325       ],
43326       [
43327         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43328         "pm",
43329         "508"
43330       ],
43331       [
43332         "Saint Vincent and the Grenadines",
43333         "vc",
43334         "1784"
43335       ],
43336       [
43337         "Samoa",
43338         "ws",
43339         "685"
43340       ],
43341       [
43342         "San Marino",
43343         "sm",
43344         "378"
43345       ],
43346       [
43347         "São Tomé and Príncipe (São Tomé e Príncipe)",
43348         "st",
43349         "239"
43350       ],
43351       [
43352         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43353         "sa",
43354         "966"
43355       ],
43356       [
43357         "Senegal (Sénégal)",
43358         "sn",
43359         "221"
43360       ],
43361       [
43362         "Serbia (Србија)",
43363         "rs",
43364         "381"
43365       ],
43366       [
43367         "Seychelles",
43368         "sc",
43369         "248"
43370       ],
43371       [
43372         "Sierra Leone",
43373         "sl",
43374         "232"
43375       ],
43376       [
43377         "Singapore",
43378         "sg",
43379         "65"
43380       ],
43381       [
43382         "Sint Maarten",
43383         "sx",
43384         "1721"
43385       ],
43386       [
43387         "Slovakia (Slovensko)",
43388         "sk",
43389         "421"
43390       ],
43391       [
43392         "Slovenia (Slovenija)",
43393         "si",
43394         "386"
43395       ],
43396       [
43397         "Solomon Islands",
43398         "sb",
43399         "677"
43400       ],
43401       [
43402         "Somalia (Soomaaliya)",
43403         "so",
43404         "252"
43405       ],
43406       [
43407         "South Africa",
43408         "za",
43409         "27"
43410       ],
43411       [
43412         "South Korea (대한민국)",
43413         "kr",
43414         "82"
43415       ],
43416       [
43417         "South Sudan (‫جنوب السودان‬‎)",
43418         "ss",
43419         "211"
43420       ],
43421       [
43422         "Spain (España)",
43423         "es",
43424         "34"
43425       ],
43426       [
43427         "Sri Lanka (ශ්‍රී ලංකාව)",
43428         "lk",
43429         "94"
43430       ],
43431       [
43432         "Sudan (‫السودان‬‎)",
43433         "sd",
43434         "249"
43435       ],
43436       [
43437         "Suriname",
43438         "sr",
43439         "597"
43440       ],
43441       [
43442         "Svalbard and Jan Mayen",
43443         "sj",
43444         "47",
43445         1
43446       ],
43447       [
43448         "Swaziland",
43449         "sz",
43450         "268"
43451       ],
43452       [
43453         "Sweden (Sverige)",
43454         "se",
43455         "46"
43456       ],
43457       [
43458         "Switzerland (Schweiz)",
43459         "ch",
43460         "41"
43461       ],
43462       [
43463         "Syria (‫سوريا‬‎)",
43464         "sy",
43465         "963"
43466       ],
43467       [
43468         "Taiwan (台灣)",
43469         "tw",
43470         "886"
43471       ],
43472       [
43473         "Tajikistan",
43474         "tj",
43475         "992"
43476       ],
43477       [
43478         "Tanzania",
43479         "tz",
43480         "255"
43481       ],
43482       [
43483         "Thailand (ไทย)",
43484         "th",
43485         "66"
43486       ],
43487       [
43488         "Timor-Leste",
43489         "tl",
43490         "670"
43491       ],
43492       [
43493         "Togo",
43494         "tg",
43495         "228"
43496       ],
43497       [
43498         "Tokelau",
43499         "tk",
43500         "690"
43501       ],
43502       [
43503         "Tonga",
43504         "to",
43505         "676"
43506       ],
43507       [
43508         "Trinidad and Tobago",
43509         "tt",
43510         "1868"
43511       ],
43512       [
43513         "Tunisia (‫تونس‬‎)",
43514         "tn",
43515         "216"
43516       ],
43517       [
43518         "Turkey (Türkiye)",
43519         "tr",
43520         "90"
43521       ],
43522       [
43523         "Turkmenistan",
43524         "tm",
43525         "993"
43526       ],
43527       [
43528         "Turks and Caicos Islands",
43529         "tc",
43530         "1649"
43531       ],
43532       [
43533         "Tuvalu",
43534         "tv",
43535         "688"
43536       ],
43537       [
43538         "U.S. Virgin Islands",
43539         "vi",
43540         "1340"
43541       ],
43542       [
43543         "Uganda",
43544         "ug",
43545         "256"
43546       ],
43547       [
43548         "Ukraine (Україна)",
43549         "ua",
43550         "380"
43551       ],
43552       [
43553         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43554         "ae",
43555         "971"
43556       ],
43557       [
43558         "United Kingdom",
43559         "gb",
43560         "44",
43561         0
43562       ],
43563       [
43564         "United States",
43565         "us",
43566         "1",
43567         0
43568       ],
43569       [
43570         "Uruguay",
43571         "uy",
43572         "598"
43573       ],
43574       [
43575         "Uzbekistan (Oʻzbekiston)",
43576         "uz",
43577         "998"
43578       ],
43579       [
43580         "Vanuatu",
43581         "vu",
43582         "678"
43583       ],
43584       [
43585         "Vatican City (Città del Vaticano)",
43586         "va",
43587         "39",
43588         1
43589       ],
43590       [
43591         "Venezuela",
43592         "ve",
43593         "58"
43594       ],
43595       [
43596         "Vietnam (Việt Nam)",
43597         "vn",
43598         "84"
43599       ],
43600       [
43601         "Wallis and Futuna (Wallis-et-Futuna)",
43602         "wf",
43603         "681"
43604       ],
43605       [
43606         "Western Sahara (‫الصحراء الغربية‬‎)",
43607         "eh",
43608         "212",
43609         1
43610       ],
43611       [
43612         "Yemen (‫اليمن‬‎)",
43613         "ye",
43614         "967"
43615       ],
43616       [
43617         "Zambia",
43618         "zm",
43619         "260"
43620       ],
43621       [
43622         "Zimbabwe",
43623         "zw",
43624         "263"
43625       ],
43626       [
43627         "Åland Islands",
43628         "ax",
43629         "358",
43630         1
43631       ]
43632   ];
43633   
43634   return d;
43635 }/**
43636 *    This script refer to:
43637 *    Title: International Telephone Input
43638 *    Author: Jack O'Connor
43639 *    Code version:  v12.1.12
43640 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43641 **/
43642
43643 /**
43644  * @class Roo.bootstrap.PhoneInput
43645  * @extends Roo.bootstrap.TriggerField
43646  * An input with International dial-code selection
43647  
43648  * @cfg {String} defaultDialCode default '+852'
43649  * @cfg {Array} preferedCountries default []
43650   
43651  * @constructor
43652  * Create a new PhoneInput.
43653  * @param {Object} config Configuration options
43654  */
43655
43656 Roo.bootstrap.PhoneInput = function(config) {
43657     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43658 };
43659
43660 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43661         /**
43662         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43663         */
43664         listWidth: undefined,
43665         
43666         selectedClass: 'active',
43667         
43668         invalidClass : "has-warning",
43669         
43670         validClass: 'has-success',
43671         
43672         allowed: '0123456789',
43673         
43674         max_length: 15,
43675         
43676         /**
43677          * @cfg {String} defaultDialCode The default dial code when initializing the input
43678          */
43679         defaultDialCode: '+852',
43680         
43681         /**
43682          * @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
43683          */
43684         preferedCountries: false,
43685         
43686         getAutoCreate : function()
43687         {
43688             var data = Roo.bootstrap.PhoneInputData();
43689             var align = this.labelAlign || this.parentLabelAlign();
43690             var id = Roo.id();
43691             
43692             this.allCountries = [];
43693             this.dialCodeMapping = [];
43694             
43695             for (var i = 0; i < data.length; i++) {
43696               var c = data[i];
43697               this.allCountries[i] = {
43698                 name: c[0],
43699                 iso2: c[1],
43700                 dialCode: c[2],
43701                 priority: c[3] || 0,
43702                 areaCodes: c[4] || null
43703               };
43704               this.dialCodeMapping[c[2]] = {
43705                   name: c[0],
43706                   iso2: c[1],
43707                   priority: c[3] || 0,
43708                   areaCodes: c[4] || null
43709               };
43710             }
43711             
43712             var cfg = {
43713                 cls: 'form-group',
43714                 cn: []
43715             };
43716             
43717             var input =  {
43718                 tag: 'input',
43719                 id : id,
43720                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43721                 maxlength: this.max_length,
43722                 cls : 'form-control tel-input',
43723                 autocomplete: 'new-password'
43724             };
43725             
43726             var hiddenInput = {
43727                 tag: 'input',
43728                 type: 'hidden',
43729                 cls: 'hidden-tel-input'
43730             };
43731             
43732             if (this.name) {
43733                 hiddenInput.name = this.name;
43734             }
43735             
43736             if (this.disabled) {
43737                 input.disabled = true;
43738             }
43739             
43740             var flag_container = {
43741                 tag: 'div',
43742                 cls: 'flag-box',
43743                 cn: [
43744                     {
43745                         tag: 'div',
43746                         cls: 'flag'
43747                     },
43748                     {
43749                         tag: 'div',
43750                         cls: 'caret'
43751                     }
43752                 ]
43753             };
43754             
43755             var box = {
43756                 tag: 'div',
43757                 cls: this.hasFeedback ? 'has-feedback' : '',
43758                 cn: [
43759                     hiddenInput,
43760                     input,
43761                     {
43762                         tag: 'input',
43763                         cls: 'dial-code-holder',
43764                         disabled: true
43765                     }
43766                 ]
43767             };
43768             
43769             var container = {
43770                 cls: 'roo-select2-container input-group',
43771                 cn: [
43772                     flag_container,
43773                     box
43774                 ]
43775             };
43776             
43777             if (this.fieldLabel.length) {
43778                 var indicator = {
43779                     tag: 'i',
43780                     tooltip: 'This field is required'
43781                 };
43782                 
43783                 var label = {
43784                     tag: 'label',
43785                     'for':  id,
43786                     cls: 'control-label',
43787                     cn: []
43788                 };
43789                 
43790                 var label_text = {
43791                     tag: 'span',
43792                     html: this.fieldLabel
43793                 };
43794                 
43795                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43796                 label.cn = [
43797                     indicator,
43798                     label_text
43799                 ];
43800                 
43801                 if(this.indicatorpos == 'right') {
43802                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43803                     label.cn = [
43804                         label_text,
43805                         indicator
43806                     ];
43807                 }
43808                 
43809                 if(align == 'left') {
43810                     container = {
43811                         tag: 'div',
43812                         cn: [
43813                             container
43814                         ]
43815                     };
43816                     
43817                     if(this.labelWidth > 12){
43818                         label.style = "width: " + this.labelWidth + 'px';
43819                     }
43820                     if(this.labelWidth < 13 && this.labelmd == 0){
43821                         this.labelmd = this.labelWidth;
43822                     }
43823                     if(this.labellg > 0){
43824                         label.cls += ' col-lg-' + this.labellg;
43825                         input.cls += ' col-lg-' + (12 - this.labellg);
43826                     }
43827                     if(this.labelmd > 0){
43828                         label.cls += ' col-md-' + this.labelmd;
43829                         container.cls += ' col-md-' + (12 - this.labelmd);
43830                     }
43831                     if(this.labelsm > 0){
43832                         label.cls += ' col-sm-' + this.labelsm;
43833                         container.cls += ' col-sm-' + (12 - this.labelsm);
43834                     }
43835                     if(this.labelxs > 0){
43836                         label.cls += ' col-xs-' + this.labelxs;
43837                         container.cls += ' col-xs-' + (12 - this.labelxs);
43838                     }
43839                 }
43840             }
43841             
43842             cfg.cn = [
43843                 label,
43844                 container
43845             ];
43846             
43847             var settings = this;
43848             
43849             ['xs','sm','md','lg'].map(function(size){
43850                 if (settings[size]) {
43851                     cfg.cls += ' col-' + size + '-' + settings[size];
43852                 }
43853             });
43854             
43855             this.store = new Roo.data.Store({
43856                 proxy : new Roo.data.MemoryProxy({}),
43857                 reader : new Roo.data.JsonReader({
43858                     fields : [
43859                         {
43860                             'name' : 'name',
43861                             'type' : 'string'
43862                         },
43863                         {
43864                             'name' : 'iso2',
43865                             'type' : 'string'
43866                         },
43867                         {
43868                             'name' : 'dialCode',
43869                             'type' : 'string'
43870                         },
43871                         {
43872                             'name' : 'priority',
43873                             'type' : 'string'
43874                         },
43875                         {
43876                             'name' : 'areaCodes',
43877                             'type' : 'string'
43878                         }
43879                     ]
43880                 })
43881             });
43882             
43883             if(!this.preferedCountries) {
43884                 this.preferedCountries = [
43885                     'hk',
43886                     'gb',
43887                     'us'
43888                 ];
43889             }
43890             
43891             var p = this.preferedCountries.reverse();
43892             
43893             if(p) {
43894                 for (var i = 0; i < p.length; i++) {
43895                     for (var j = 0; j < this.allCountries.length; j++) {
43896                         if(this.allCountries[j].iso2 == p[i]) {
43897                             var t = this.allCountries[j];
43898                             this.allCountries.splice(j,1);
43899                             this.allCountries.unshift(t);
43900                         }
43901                     } 
43902                 }
43903             }
43904             
43905             this.store.proxy.data = {
43906                 success: true,
43907                 data: this.allCountries
43908             };
43909             
43910             return cfg;
43911         },
43912         
43913         initEvents : function()
43914         {
43915             this.createList();
43916             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43917             
43918             this.indicator = this.indicatorEl();
43919             this.flag = this.flagEl();
43920             this.dialCodeHolder = this.dialCodeHolderEl();
43921             
43922             this.trigger = this.el.select('div.flag-box',true).first();
43923             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43924             
43925             var _this = this;
43926             
43927             (function(){
43928                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43929                 _this.list.setWidth(lw);
43930             }).defer(100);
43931             
43932             this.list.on('mouseover', this.onViewOver, this);
43933             this.list.on('mousemove', this.onViewMove, this);
43934             this.inputEl().on("keyup", this.onKeyUp, this);
43935             this.inputEl().on("keypress", this.onKeyPress, this);
43936             
43937             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43938
43939             this.view = new Roo.View(this.list, this.tpl, {
43940                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43941             });
43942             
43943             this.view.on('click', this.onViewClick, this);
43944             this.setValue(this.defaultDialCode);
43945         },
43946         
43947         onTriggerClick : function(e)
43948         {
43949             Roo.log('trigger click');
43950             if(this.disabled){
43951                 return;
43952             }
43953             
43954             if(this.isExpanded()){
43955                 this.collapse();
43956                 this.hasFocus = false;
43957             }else {
43958                 this.store.load({});
43959                 this.hasFocus = true;
43960                 this.expand();
43961             }
43962         },
43963         
43964         isExpanded : function()
43965         {
43966             return this.list.isVisible();
43967         },
43968         
43969         collapse : function()
43970         {
43971             if(!this.isExpanded()){
43972                 return;
43973             }
43974             this.list.hide();
43975             Roo.get(document).un('mousedown', this.collapseIf, this);
43976             Roo.get(document).un('mousewheel', this.collapseIf, this);
43977             this.fireEvent('collapse', this);
43978             this.validate();
43979         },
43980         
43981         expand : function()
43982         {
43983             Roo.log('expand');
43984
43985             if(this.isExpanded() || !this.hasFocus){
43986                 return;
43987             }
43988             
43989             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43990             this.list.setWidth(lw);
43991             
43992             this.list.show();
43993             this.restrictHeight();
43994             
43995             Roo.get(document).on('mousedown', this.collapseIf, this);
43996             Roo.get(document).on('mousewheel', this.collapseIf, this);
43997             
43998             this.fireEvent('expand', this);
43999         },
44000         
44001         restrictHeight : function()
44002         {
44003             this.list.alignTo(this.inputEl(), this.listAlign);
44004             this.list.alignTo(this.inputEl(), this.listAlign);
44005         },
44006         
44007         onViewOver : function(e, t)
44008         {
44009             if(this.inKeyMode){
44010                 return;
44011             }
44012             var item = this.view.findItemFromChild(t);
44013             
44014             if(item){
44015                 var index = this.view.indexOf(item);
44016                 this.select(index, false);
44017             }
44018         },
44019
44020         // private
44021         onViewClick : function(view, doFocus, el, e)
44022         {
44023             var index = this.view.getSelectedIndexes()[0];
44024             
44025             var r = this.store.getAt(index);
44026             
44027             if(r){
44028                 this.onSelect(r, index);
44029             }
44030             if(doFocus !== false && !this.blockFocus){
44031                 this.inputEl().focus();
44032             }
44033         },
44034         
44035         onViewMove : function(e, t)
44036         {
44037             this.inKeyMode = false;
44038         },
44039         
44040         select : function(index, scrollIntoView)
44041         {
44042             this.selectedIndex = index;
44043             this.view.select(index);
44044             if(scrollIntoView !== false){
44045                 var el = this.view.getNode(index);
44046                 if(el){
44047                     this.list.scrollChildIntoView(el, false);
44048                 }
44049             }
44050         },
44051         
44052         createList : function()
44053         {
44054             this.list = Roo.get(document.body).createChild({
44055                 tag: 'ul',
44056                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44057                 style: 'display:none'
44058             });
44059             
44060             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44061         },
44062         
44063         collapseIf : function(e)
44064         {
44065             var in_combo  = e.within(this.el);
44066             var in_list =  e.within(this.list);
44067             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44068             
44069             if (in_combo || in_list || is_list) {
44070                 return;
44071             }
44072             this.collapse();
44073         },
44074         
44075         onSelect : function(record, index)
44076         {
44077             if(this.fireEvent('beforeselect', this, record, index) !== false){
44078                 
44079                 this.setFlagClass(record.data.iso2);
44080                 this.setDialCode(record.data.dialCode);
44081                 this.hasFocus = false;
44082                 this.collapse();
44083                 this.fireEvent('select', this, record, index);
44084             }
44085         },
44086         
44087         flagEl : function()
44088         {
44089             var flag = this.el.select('div.flag',true).first();
44090             if(!flag){
44091                 return false;
44092             }
44093             return flag;
44094         },
44095         
44096         dialCodeHolderEl : function()
44097         {
44098             var d = this.el.select('input.dial-code-holder',true).first();
44099             if(!d){
44100                 return false;
44101             }
44102             return d;
44103         },
44104         
44105         setDialCode : function(v)
44106         {
44107             this.dialCodeHolder.dom.value = '+'+v;
44108         },
44109         
44110         setFlagClass : function(n)
44111         {
44112             this.flag.dom.className = 'flag '+n;
44113         },
44114         
44115         getValue : function()
44116         {
44117             var v = this.inputEl().getValue();
44118             if(this.dialCodeHolder) {
44119                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44120             }
44121             return v;
44122         },
44123         
44124         setValue : function(v)
44125         {
44126             var d = this.getDialCode(v);
44127             
44128             //invalid dial code
44129             if(v.length == 0 || !d || d.length == 0) {
44130                 if(this.rendered){
44131                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44132                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44133                 }
44134                 return;
44135             }
44136             
44137             //valid dial code
44138             this.setFlagClass(this.dialCodeMapping[d].iso2);
44139             this.setDialCode(d);
44140             this.inputEl().dom.value = v.replace('+'+d,'');
44141             this.hiddenEl().dom.value = this.getValue();
44142             
44143             this.validate();
44144         },
44145         
44146         getDialCode : function(v)
44147         {
44148             v = v ||  '';
44149             
44150             if (v.length == 0) {
44151                 return this.dialCodeHolder.dom.value;
44152             }
44153             
44154             var dialCode = "";
44155             if (v.charAt(0) != "+") {
44156                 return false;
44157             }
44158             var numericChars = "";
44159             for (var i = 1; i < v.length; i++) {
44160               var c = v.charAt(i);
44161               if (!isNaN(c)) {
44162                 numericChars += c;
44163                 if (this.dialCodeMapping[numericChars]) {
44164                   dialCode = v.substr(1, i);
44165                 }
44166                 if (numericChars.length == 4) {
44167                   break;
44168                 }
44169               }
44170             }
44171             return dialCode;
44172         },
44173         
44174         reset : function()
44175         {
44176             this.setValue(this.defaultDialCode);
44177             this.validate();
44178         },
44179         
44180         hiddenEl : function()
44181         {
44182             return this.el.select('input.hidden-tel-input',true).first();
44183         },
44184         
44185         // after setting val
44186         onKeyUp : function(e){
44187             this.setValue(this.getValue());
44188         },
44189         
44190         onKeyPress : function(e){
44191             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44192                 e.stopEvent();
44193             }
44194         }
44195         
44196 });
44197 /**
44198  * @class Roo.bootstrap.MoneyField
44199  * @extends Roo.bootstrap.ComboBox
44200  * Bootstrap MoneyField class
44201  * 
44202  * @constructor
44203  * Create a new MoneyField.
44204  * @param {Object} config Configuration options
44205  */
44206
44207 Roo.bootstrap.MoneyField = function(config) {
44208     
44209     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44210     
44211 };
44212
44213 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44214     
44215     /**
44216      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44217      */
44218     allowDecimals : true,
44219     /**
44220      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44221      */
44222     decimalSeparator : ".",
44223     /**
44224      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44225      */
44226     decimalPrecision : 0,
44227     /**
44228      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44229      */
44230     allowNegative : true,
44231     /**
44232      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44233      */
44234     allowZero: true,
44235     /**
44236      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44237      */
44238     minValue : Number.NEGATIVE_INFINITY,
44239     /**
44240      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44241      */
44242     maxValue : Number.MAX_VALUE,
44243     /**
44244      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44245      */
44246     minText : "The minimum value for this field is {0}",
44247     /**
44248      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44249      */
44250     maxText : "The maximum value for this field is {0}",
44251     /**
44252      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44253      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44254      */
44255     nanText : "{0} is not a valid number",
44256     /**
44257      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44258      */
44259     castInt : true,
44260     /**
44261      * @cfg {String} defaults currency of the MoneyField
44262      * value should be in lkey
44263      */
44264     defaultCurrency : false,
44265     /**
44266      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44267      */
44268     thousandsDelimiter : false,
44269     /**
44270      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44271      */
44272     max_length: false,
44273     
44274     inputlg : 9,
44275     inputmd : 9,
44276     inputsm : 9,
44277     inputxs : 6,
44278     
44279     store : false,
44280     
44281     getAutoCreate : function()
44282     {
44283         var align = this.labelAlign || this.parentLabelAlign();
44284         
44285         var id = Roo.id();
44286
44287         var cfg = {
44288             cls: 'form-group',
44289             cn: []
44290         };
44291
44292         var input =  {
44293             tag: 'input',
44294             id : id,
44295             cls : 'form-control roo-money-amount-input',
44296             autocomplete: 'new-password'
44297         };
44298         
44299         var hiddenInput = {
44300             tag: 'input',
44301             type: 'hidden',
44302             id: Roo.id(),
44303             cls: 'hidden-number-input'
44304         };
44305         
44306         if(this.max_length) {
44307             input.maxlength = this.max_length; 
44308         }
44309         
44310         if (this.name) {
44311             hiddenInput.name = this.name;
44312         }
44313
44314         if (this.disabled) {
44315             input.disabled = true;
44316         }
44317
44318         var clg = 12 - this.inputlg;
44319         var cmd = 12 - this.inputmd;
44320         var csm = 12 - this.inputsm;
44321         var cxs = 12 - this.inputxs;
44322         
44323         var container = {
44324             tag : 'div',
44325             cls : 'row roo-money-field',
44326             cn : [
44327                 {
44328                     tag : 'div',
44329                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44330                     cn : [
44331                         {
44332                             tag : 'div',
44333                             cls: 'roo-select2-container input-group',
44334                             cn: [
44335                                 {
44336                                     tag : 'input',
44337                                     cls : 'form-control roo-money-currency-input',
44338                                     autocomplete: 'new-password',
44339                                     readOnly : 1,
44340                                     name : this.currencyName
44341                                 },
44342                                 {
44343                                     tag :'span',
44344                                     cls : 'input-group-addon',
44345                                     cn : [
44346                                         {
44347                                             tag: 'span',
44348                                             cls: 'caret'
44349                                         }
44350                                     ]
44351                                 }
44352                             ]
44353                         }
44354                     ]
44355                 },
44356                 {
44357                     tag : 'div',
44358                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44359                     cn : [
44360                         {
44361                             tag: 'div',
44362                             cls: this.hasFeedback ? 'has-feedback' : '',
44363                             cn: [
44364                                 input
44365                             ]
44366                         }
44367                     ]
44368                 }
44369             ]
44370             
44371         };
44372         
44373         if (this.fieldLabel.length) {
44374             var indicator = {
44375                 tag: 'i',
44376                 tooltip: 'This field is required'
44377             };
44378
44379             var label = {
44380                 tag: 'label',
44381                 'for':  id,
44382                 cls: 'control-label',
44383                 cn: []
44384             };
44385
44386             var label_text = {
44387                 tag: 'span',
44388                 html: this.fieldLabel
44389             };
44390
44391             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44392             label.cn = [
44393                 indicator,
44394                 label_text
44395             ];
44396
44397             if(this.indicatorpos == 'right') {
44398                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44399                 label.cn = [
44400                     label_text,
44401                     indicator
44402                 ];
44403             }
44404
44405             if(align == 'left') {
44406                 container = {
44407                     tag: 'div',
44408                     cn: [
44409                         container
44410                     ]
44411                 };
44412
44413                 if(this.labelWidth > 12){
44414                     label.style = "width: " + this.labelWidth + 'px';
44415                 }
44416                 if(this.labelWidth < 13 && this.labelmd == 0){
44417                     this.labelmd = this.labelWidth;
44418                 }
44419                 if(this.labellg > 0){
44420                     label.cls += ' col-lg-' + this.labellg;
44421                     input.cls += ' col-lg-' + (12 - this.labellg);
44422                 }
44423                 if(this.labelmd > 0){
44424                     label.cls += ' col-md-' + this.labelmd;
44425                     container.cls += ' col-md-' + (12 - this.labelmd);
44426                 }
44427                 if(this.labelsm > 0){
44428                     label.cls += ' col-sm-' + this.labelsm;
44429                     container.cls += ' col-sm-' + (12 - this.labelsm);
44430                 }
44431                 if(this.labelxs > 0){
44432                     label.cls += ' col-xs-' + this.labelxs;
44433                     container.cls += ' col-xs-' + (12 - this.labelxs);
44434                 }
44435             }
44436         }
44437
44438         cfg.cn = [
44439             label,
44440             container,
44441             hiddenInput
44442         ];
44443         
44444         var settings = this;
44445
44446         ['xs','sm','md','lg'].map(function(size){
44447             if (settings[size]) {
44448                 cfg.cls += ' col-' + size + '-' + settings[size];
44449             }
44450         });
44451         
44452         return cfg;
44453     },
44454     
44455     initEvents : function()
44456     {
44457         this.indicator = this.indicatorEl();
44458         
44459         this.initCurrencyEvent();
44460         
44461         this.initNumberEvent();
44462     },
44463     
44464     initCurrencyEvent : function()
44465     {
44466         if (!this.store) {
44467             throw "can not find store for combo";
44468         }
44469         
44470         this.store = Roo.factory(this.store, Roo.data);
44471         this.store.parent = this;
44472         
44473         this.createList();
44474         
44475         this.triggerEl = this.el.select('.input-group-addon', true).first();
44476         
44477         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44478         
44479         var _this = this;
44480         
44481         (function(){
44482             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44483             _this.list.setWidth(lw);
44484         }).defer(100);
44485         
44486         this.list.on('mouseover', this.onViewOver, this);
44487         this.list.on('mousemove', this.onViewMove, this);
44488         this.list.on('scroll', this.onViewScroll, this);
44489         
44490         if(!this.tpl){
44491             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44492         }
44493         
44494         this.view = new Roo.View(this.list, this.tpl, {
44495             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44496         });
44497         
44498         this.view.on('click', this.onViewClick, this);
44499         
44500         this.store.on('beforeload', this.onBeforeLoad, this);
44501         this.store.on('load', this.onLoad, this);
44502         this.store.on('loadexception', this.onLoadException, this);
44503         
44504         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44505             "up" : function(e){
44506                 this.inKeyMode = true;
44507                 this.selectPrev();
44508             },
44509
44510             "down" : function(e){
44511                 if(!this.isExpanded()){
44512                     this.onTriggerClick();
44513                 }else{
44514                     this.inKeyMode = true;
44515                     this.selectNext();
44516                 }
44517             },
44518
44519             "enter" : function(e){
44520                 this.collapse();
44521                 
44522                 if(this.fireEvent("specialkey", this, e)){
44523                     this.onViewClick(false);
44524                 }
44525                 
44526                 return true;
44527             },
44528
44529             "esc" : function(e){
44530                 this.collapse();
44531             },
44532
44533             "tab" : function(e){
44534                 this.collapse();
44535                 
44536                 if(this.fireEvent("specialkey", this, e)){
44537                     this.onViewClick(false);
44538                 }
44539                 
44540                 return true;
44541             },
44542
44543             scope : this,
44544
44545             doRelay : function(foo, bar, hname){
44546                 if(hname == 'down' || this.scope.isExpanded()){
44547                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44548                 }
44549                 return true;
44550             },
44551
44552             forceKeyDown: true
44553         });
44554         
44555         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44556         
44557     },
44558     
44559     initNumberEvent : function(e)
44560     {
44561         this.inputEl().on("keydown" , this.fireKey,  this);
44562         this.inputEl().on("focus", this.onFocus,  this);
44563         this.inputEl().on("blur", this.onBlur,  this);
44564         
44565         this.inputEl().relayEvent('keyup', this);
44566         
44567         if(this.indicator){
44568             this.indicator.addClass('invisible');
44569         }
44570  
44571         this.originalValue = this.getValue();
44572         
44573         if(this.validationEvent == 'keyup'){
44574             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44575             this.inputEl().on('keyup', this.filterValidation, this);
44576         }
44577         else if(this.validationEvent !== false){
44578             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44579         }
44580         
44581         if(this.selectOnFocus){
44582             this.on("focus", this.preFocus, this);
44583             
44584         }
44585         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44586             this.inputEl().on("keypress", this.filterKeys, this);
44587         } else {
44588             this.inputEl().relayEvent('keypress', this);
44589         }
44590         
44591         var allowed = "0123456789";
44592         
44593         if(this.allowDecimals){
44594             allowed += this.decimalSeparator;
44595         }
44596         
44597         if(this.allowNegative){
44598             allowed += "-";
44599         }
44600         
44601         if(this.thousandsDelimiter) {
44602             allowed += ",";
44603         }
44604         
44605         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44606         
44607         var keyPress = function(e){
44608             
44609             var k = e.getKey();
44610             
44611             var c = e.getCharCode();
44612             
44613             if(
44614                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44615                     allowed.indexOf(String.fromCharCode(c)) === -1
44616             ){
44617                 e.stopEvent();
44618                 return;
44619             }
44620             
44621             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44622                 return;
44623             }
44624             
44625             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44626                 e.stopEvent();
44627             }
44628         };
44629         
44630         this.inputEl().on("keypress", keyPress, this);
44631         
44632     },
44633     
44634     onTriggerClick : function(e)
44635     {   
44636         if(this.disabled){
44637             return;
44638         }
44639         
44640         this.page = 0;
44641         this.loadNext = false;
44642         
44643         if(this.isExpanded()){
44644             this.collapse();
44645             return;
44646         }
44647         
44648         this.hasFocus = true;
44649         
44650         if(this.triggerAction == 'all') {
44651             this.doQuery(this.allQuery, true);
44652             return;
44653         }
44654         
44655         this.doQuery(this.getRawValue());
44656     },
44657     
44658     getCurrency : function()
44659     {   
44660         var v = this.currencyEl().getValue();
44661         
44662         return v;
44663     },
44664     
44665     restrictHeight : function()
44666     {
44667         this.list.alignTo(this.currencyEl(), this.listAlign);
44668         this.list.alignTo(this.currencyEl(), this.listAlign);
44669     },
44670     
44671     onViewClick : function(view, doFocus, el, e)
44672     {
44673         var index = this.view.getSelectedIndexes()[0];
44674         
44675         var r = this.store.getAt(index);
44676         
44677         if(r){
44678             this.onSelect(r, index);
44679         }
44680     },
44681     
44682     onSelect : function(record, index){
44683         
44684         if(this.fireEvent('beforeselect', this, record, index) !== false){
44685         
44686             this.setFromCurrencyData(index > -1 ? record.data : false);
44687             
44688             this.collapse();
44689             
44690             this.fireEvent('select', this, record, index);
44691         }
44692     },
44693     
44694     setFromCurrencyData : function(o)
44695     {
44696         var currency = '';
44697         
44698         this.lastCurrency = o;
44699         
44700         if (this.currencyField) {
44701             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44702         } else {
44703             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44704         }
44705         
44706         this.lastSelectionText = currency;
44707         
44708         //setting default currency
44709         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44710             this.setCurrency(this.defaultCurrency);
44711             return;
44712         }
44713         
44714         this.setCurrency(currency);
44715     },
44716     
44717     setFromData : function(o)
44718     {
44719         var c = {};
44720         
44721         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44722         
44723         this.setFromCurrencyData(c);
44724         
44725         var value = '';
44726         
44727         if (this.name) {
44728             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44729         } else {
44730             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44731         }
44732         
44733         this.setValue(value);
44734         
44735     },
44736     
44737     setCurrency : function(v)
44738     {   
44739         this.currencyValue = v;
44740         
44741         if(this.rendered){
44742             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44743             this.validate();
44744         }
44745     },
44746     
44747     setValue : function(v)
44748     {
44749         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44750         
44751         this.value = v;
44752         
44753         if(this.rendered){
44754             
44755             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44756             
44757             this.inputEl().dom.value = (v == '') ? '' :
44758                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44759             
44760             if(!this.allowZero && v === '0') {
44761                 this.hiddenEl().dom.value = '';
44762                 this.inputEl().dom.value = '';
44763             }
44764             
44765             this.validate();
44766         }
44767     },
44768     
44769     getRawValue : function()
44770     {
44771         var v = this.inputEl().getValue();
44772         
44773         return v;
44774     },
44775     
44776     getValue : function()
44777     {
44778         return this.fixPrecision(this.parseValue(this.getRawValue()));
44779     },
44780     
44781     parseValue : function(value)
44782     {
44783         if(this.thousandsDelimiter) {
44784             value += "";
44785             r = new RegExp(",", "g");
44786             value = value.replace(r, "");
44787         }
44788         
44789         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44790         return isNaN(value) ? '' : value;
44791         
44792     },
44793     
44794     fixPrecision : function(value)
44795     {
44796         if(this.thousandsDelimiter) {
44797             value += "";
44798             r = new RegExp(",", "g");
44799             value = value.replace(r, "");
44800         }
44801         
44802         var nan = isNaN(value);
44803         
44804         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44805             return nan ? '' : value;
44806         }
44807         return parseFloat(value).toFixed(this.decimalPrecision);
44808     },
44809     
44810     decimalPrecisionFcn : function(v)
44811     {
44812         return Math.floor(v);
44813     },
44814     
44815     validateValue : function(value)
44816     {
44817         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44818             return false;
44819         }
44820         
44821         var num = this.parseValue(value);
44822         
44823         if(isNaN(num)){
44824             this.markInvalid(String.format(this.nanText, value));
44825             return false;
44826         }
44827         
44828         if(num < this.minValue){
44829             this.markInvalid(String.format(this.minText, this.minValue));
44830             return false;
44831         }
44832         
44833         if(num > this.maxValue){
44834             this.markInvalid(String.format(this.maxText, this.maxValue));
44835             return false;
44836         }
44837         
44838         return true;
44839     },
44840     
44841     validate : function()
44842     {
44843         if(this.disabled || this.allowBlank){
44844             this.markValid();
44845             return true;
44846         }
44847         
44848         var currency = this.getCurrency();
44849         
44850         if(this.validateValue(this.getRawValue()) && currency.length){
44851             this.markValid();
44852             return true;
44853         }
44854         
44855         this.markInvalid();
44856         return false;
44857     },
44858     
44859     getName: function()
44860     {
44861         return this.name;
44862     },
44863     
44864     beforeBlur : function()
44865     {
44866         if(!this.castInt){
44867             return;
44868         }
44869         
44870         var v = this.parseValue(this.getRawValue());
44871         
44872         if(v || v == 0){
44873             this.setValue(v);
44874         }
44875     },
44876     
44877     onBlur : function()
44878     {
44879         this.beforeBlur();
44880         
44881         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44882             //this.el.removeClass(this.focusClass);
44883         }
44884         
44885         this.hasFocus = false;
44886         
44887         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44888             this.validate();
44889         }
44890         
44891         var v = this.getValue();
44892         
44893         if(String(v) !== String(this.startValue)){
44894             this.fireEvent('change', this, v, this.startValue);
44895         }
44896         
44897         this.fireEvent("blur", this);
44898     },
44899     
44900     inputEl : function()
44901     {
44902         return this.el.select('.roo-money-amount-input', true).first();
44903     },
44904     
44905     currencyEl : function()
44906     {
44907         return this.el.select('.roo-money-currency-input', true).first();
44908     },
44909     
44910     hiddenEl : function()
44911     {
44912         return this.el.select('input.hidden-number-input',true).first();
44913     }
44914     
44915 });/**
44916  * @class Roo.bootstrap.BezierSignature
44917  * @extends Roo.bootstrap.Component
44918  * Bootstrap BezierSignature class
44919  * This script refer to:
44920  *    Title: Signature Pad
44921  *    Author: szimek
44922  *    Availability: https://github.com/szimek/signature_pad
44923  *
44924  * @constructor
44925  * Create a new BezierSignature
44926  * @param {Object} config The config object
44927  */
44928
44929 Roo.bootstrap.BezierSignature = function(config){
44930     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44931     this.addEvents({
44932         "resize" : true
44933     });
44934 };
44935
44936 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44937 {
44938      
44939     curve_data: [],
44940     
44941     is_empty: true,
44942     
44943     mouse_btn_down: true,
44944     
44945     /**
44946      * @cfg {int} canvas height
44947      */
44948     canvas_height: '200px',
44949     
44950     /**
44951      * @cfg {float|function} Radius of a single dot.
44952      */ 
44953     dot_size: false,
44954     
44955     /**
44956      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44957      */
44958     min_width: 0.5,
44959     
44960     /**
44961      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44962      */
44963     max_width: 2.5,
44964     
44965     /**
44966      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44967      */
44968     throttle: 16,
44969     
44970     /**
44971      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44972      */
44973     min_distance: 5,
44974     
44975     /**
44976      * @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.
44977      */
44978     bg_color: 'rgba(0, 0, 0, 0)',
44979     
44980     /**
44981      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44982      */
44983     dot_color: 'black',
44984     
44985     /**
44986      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44987      */ 
44988     velocity_filter_weight: 0.7,
44989     
44990     /**
44991      * @cfg {function} Callback when stroke begin. 
44992      */
44993     onBegin: false,
44994     
44995     /**
44996      * @cfg {function} Callback when stroke end.
44997      */
44998     onEnd: false,
44999     
45000     getAutoCreate : function()
45001     {
45002         var cls = 'roo-signature column';
45003         
45004         if(this.cls){
45005             cls += ' ' + this.cls;
45006         }
45007         
45008         var col_sizes = [
45009             'lg',
45010             'md',
45011             'sm',
45012             'xs'
45013         ];
45014         
45015         for(var i = 0; i < col_sizes.length; i++) {
45016             if(this[col_sizes[i]]) {
45017                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45018             }
45019         }
45020         
45021         var cfg = {
45022             tag: 'div',
45023             cls: cls,
45024             cn: [
45025                 {
45026                     tag: 'div',
45027                     cls: 'roo-signature-body',
45028                     cn: [
45029                         {
45030                             tag: 'canvas',
45031                             cls: 'roo-signature-body-canvas',
45032                             height: this.canvas_height,
45033                             width: this.canvas_width
45034                         }
45035                     ]
45036                 },
45037                 {
45038                     tag: 'input',
45039                     type: 'file',
45040                     style: 'display: none'
45041                 }
45042             ]
45043         };
45044         
45045         return cfg;
45046     },
45047     
45048     initEvents: function() 
45049     {
45050         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45051         
45052         var canvas = this.canvasEl();
45053         
45054         // mouse && touch event swapping...
45055         canvas.dom.style.touchAction = 'none';
45056         canvas.dom.style.msTouchAction = 'none';
45057         
45058         this.mouse_btn_down = false;
45059         canvas.on('mousedown', this._handleMouseDown, this);
45060         canvas.on('mousemove', this._handleMouseMove, this);
45061         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45062         
45063         if (window.PointerEvent) {
45064             canvas.on('pointerdown', this._handleMouseDown, this);
45065             canvas.on('pointermove', this._handleMouseMove, this);
45066             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45067         }
45068         
45069         if ('ontouchstart' in window) {
45070             canvas.on('touchstart', this._handleTouchStart, this);
45071             canvas.on('touchmove', this._handleTouchMove, this);
45072             canvas.on('touchend', this._handleTouchEnd, this);
45073         }
45074         
45075         Roo.EventManager.onWindowResize(this.resize, this, true);
45076         
45077         // file input event
45078         this.fileEl().on('change', this.uploadImage, this);
45079         
45080         this.clear();
45081         
45082         this.resize();
45083     },
45084     
45085     resize: function(){
45086         
45087         var canvas = this.canvasEl().dom;
45088         var ctx = this.canvasElCtx();
45089         var img_data = false;
45090         
45091         if(canvas.width > 0) {
45092             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45093         }
45094         // setting canvas width will clean img data
45095         canvas.width = 0;
45096         
45097         var style = window.getComputedStyle ? 
45098             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45099             
45100         var padding_left = parseInt(style.paddingLeft) || 0;
45101         var padding_right = parseInt(style.paddingRight) || 0;
45102         
45103         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45104         
45105         if(img_data) {
45106             ctx.putImageData(img_data, 0, 0);
45107         }
45108     },
45109     
45110     _handleMouseDown: function(e)
45111     {
45112         if (e.browserEvent.which === 1) {
45113             this.mouse_btn_down = true;
45114             this.strokeBegin(e);
45115         }
45116     },
45117     
45118     _handleMouseMove: function (e)
45119     {
45120         if (this.mouse_btn_down) {
45121             this.strokeMoveUpdate(e);
45122         }
45123     },
45124     
45125     _handleMouseUp: function (e)
45126     {
45127         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45128             this.mouse_btn_down = false;
45129             this.strokeEnd(e);
45130         }
45131     },
45132     
45133     _handleTouchStart: function (e) {
45134         
45135         e.preventDefault();
45136         if (e.browserEvent.targetTouches.length === 1) {
45137             // var touch = e.browserEvent.changedTouches[0];
45138             // this.strokeBegin(touch);
45139             
45140              this.strokeBegin(e); // assume e catching the correct xy...
45141         }
45142     },
45143     
45144     _handleTouchMove: function (e) {
45145         e.preventDefault();
45146         // var touch = event.targetTouches[0];
45147         // _this._strokeMoveUpdate(touch);
45148         this.strokeMoveUpdate(e);
45149     },
45150     
45151     _handleTouchEnd: function (e) {
45152         var wasCanvasTouched = e.target === this.canvasEl().dom;
45153         if (wasCanvasTouched) {
45154             e.preventDefault();
45155             // var touch = event.changedTouches[0];
45156             // _this._strokeEnd(touch);
45157             this.strokeEnd(e);
45158         }
45159     },
45160     
45161     reset: function () {
45162         this._lastPoints = [];
45163         this._lastVelocity = 0;
45164         this._lastWidth = (this.min_width + this.max_width) / 2;
45165         this.canvasElCtx().fillStyle = this.dot_color;
45166     },
45167     
45168     strokeMoveUpdate: function(e)
45169     {
45170         this.strokeUpdate(e);
45171         
45172         if (this.throttle) {
45173             this.throttleStroke(this.strokeUpdate, this.throttle);
45174         }
45175         else {
45176             this.strokeUpdate(e);
45177         }
45178     },
45179     
45180     strokeBegin: function(e)
45181     {
45182         var newPointGroup = {
45183             color: this.dot_color,
45184             points: []
45185         };
45186         
45187         if (typeof this.onBegin === 'function') {
45188             this.onBegin(e);
45189         }
45190         
45191         this.curve_data.push(newPointGroup);
45192         this.reset();
45193         this.strokeUpdate(e);
45194     },
45195     
45196     strokeUpdate: function(e)
45197     {
45198         var rect = this.canvasEl().dom.getBoundingClientRect();
45199         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45200         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45201         var lastPoints = lastPointGroup.points;
45202         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45203         var isLastPointTooClose = lastPoint
45204             ? point.distanceTo(lastPoint) <= this.min_distance
45205             : false;
45206         var color = lastPointGroup.color;
45207         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45208             var curve = this.addPoint(point);
45209             if (!lastPoint) {
45210                 this.drawDot({color: color, point: point});
45211             }
45212             else if (curve) {
45213                 this.drawCurve({color: color, curve: curve});
45214             }
45215             lastPoints.push({
45216                 time: point.time,
45217                 x: point.x,
45218                 y: point.y
45219             });
45220         }
45221     },
45222     
45223     strokeEnd: function(e)
45224     {
45225         this.strokeUpdate(e);
45226         if (typeof this.onEnd === 'function') {
45227             this.onEnd(e);
45228         }
45229     },
45230     
45231     addPoint:  function (point) {
45232         var _lastPoints = this._lastPoints;
45233         _lastPoints.push(point);
45234         if (_lastPoints.length > 2) {
45235             if (_lastPoints.length === 3) {
45236                 _lastPoints.unshift(_lastPoints[0]);
45237             }
45238             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45239             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45240             _lastPoints.shift();
45241             return curve;
45242         }
45243         return null;
45244     },
45245     
45246     calculateCurveWidths: function (startPoint, endPoint) {
45247         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45248             (1 - this.velocity_filter_weight) * this._lastVelocity;
45249
45250         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45251         var widths = {
45252             end: newWidth,
45253             start: this._lastWidth
45254         };
45255         
45256         this._lastVelocity = velocity;
45257         this._lastWidth = newWidth;
45258         return widths;
45259     },
45260     
45261     drawDot: function (_a) {
45262         var color = _a.color, point = _a.point;
45263         var ctx = this.canvasElCtx();
45264         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45265         ctx.beginPath();
45266         this.drawCurveSegment(point.x, point.y, width);
45267         ctx.closePath();
45268         ctx.fillStyle = color;
45269         ctx.fill();
45270     },
45271     
45272     drawCurve: function (_a) {
45273         var color = _a.color, curve = _a.curve;
45274         var ctx = this.canvasElCtx();
45275         var widthDelta = curve.endWidth - curve.startWidth;
45276         var drawSteps = Math.floor(curve.length()) * 2;
45277         ctx.beginPath();
45278         ctx.fillStyle = color;
45279         for (var i = 0; i < drawSteps; i += 1) {
45280         var t = i / drawSteps;
45281         var tt = t * t;
45282         var ttt = tt * t;
45283         var u = 1 - t;
45284         var uu = u * u;
45285         var uuu = uu * u;
45286         var x = uuu * curve.startPoint.x;
45287         x += 3 * uu * t * curve.control1.x;
45288         x += 3 * u * tt * curve.control2.x;
45289         x += ttt * curve.endPoint.x;
45290         var y = uuu * curve.startPoint.y;
45291         y += 3 * uu * t * curve.control1.y;
45292         y += 3 * u * tt * curve.control2.y;
45293         y += ttt * curve.endPoint.y;
45294         var width = curve.startWidth + ttt * widthDelta;
45295         this.drawCurveSegment(x, y, width);
45296         }
45297         ctx.closePath();
45298         ctx.fill();
45299     },
45300     
45301     drawCurveSegment: function (x, y, width) {
45302         var ctx = this.canvasElCtx();
45303         ctx.moveTo(x, y);
45304         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45305         this.is_empty = false;
45306     },
45307     
45308     clear: function()
45309     {
45310         var ctx = this.canvasElCtx();
45311         var canvas = this.canvasEl().dom;
45312         ctx.fillStyle = this.bg_color;
45313         ctx.clearRect(0, 0, canvas.width, canvas.height);
45314         ctx.fillRect(0, 0, canvas.width, canvas.height);
45315         this.curve_data = [];
45316         this.reset();
45317         this.is_empty = true;
45318     },
45319     
45320     fileEl: function()
45321     {
45322         return  this.el.select('input',true).first();
45323     },
45324     
45325     canvasEl: function()
45326     {
45327         return this.el.select('canvas',true).first();
45328     },
45329     
45330     canvasElCtx: function()
45331     {
45332         return this.el.select('canvas',true).first().dom.getContext('2d');
45333     },
45334     
45335     getImage: function(type)
45336     {
45337         if(this.is_empty) {
45338             return false;
45339         }
45340         
45341         // encryption ?
45342         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45343     },
45344     
45345     drawFromImage: function(img_src)
45346     {
45347         var img = new Image();
45348         
45349         img.onload = function(){
45350             this.canvasElCtx().drawImage(img, 0, 0);
45351         }.bind(this);
45352         
45353         img.src = img_src;
45354         
45355         this.is_empty = false;
45356     },
45357     
45358     selectImage: function()
45359     {
45360         this.fileEl().dom.click();
45361     },
45362     
45363     uploadImage: function(e)
45364     {
45365         var reader = new FileReader();
45366         
45367         reader.onload = function(e){
45368             var img = new Image();
45369             img.onload = function(){
45370                 this.reset();
45371                 this.canvasElCtx().drawImage(img, 0, 0);
45372             }.bind(this);
45373             img.src = e.target.result;
45374         }.bind(this);
45375         
45376         reader.readAsDataURL(e.target.files[0]);
45377     },
45378     
45379     // Bezier Point Constructor
45380     Point: (function () {
45381         function Point(x, y, time) {
45382             this.x = x;
45383             this.y = y;
45384             this.time = time || Date.now();
45385         }
45386         Point.prototype.distanceTo = function (start) {
45387             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45388         };
45389         Point.prototype.equals = function (other) {
45390             return this.x === other.x && this.y === other.y && this.time === other.time;
45391         };
45392         Point.prototype.velocityFrom = function (start) {
45393             return this.time !== start.time
45394             ? this.distanceTo(start) / (this.time - start.time)
45395             : 0;
45396         };
45397         return Point;
45398     }()),
45399     
45400     
45401     // Bezier Constructor
45402     Bezier: (function () {
45403         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45404             this.startPoint = startPoint;
45405             this.control2 = control2;
45406             this.control1 = control1;
45407             this.endPoint = endPoint;
45408             this.startWidth = startWidth;
45409             this.endWidth = endWidth;
45410         }
45411         Bezier.fromPoints = function (points, widths, scope) {
45412             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45413             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45414             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45415         };
45416         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45417             var dx1 = s1.x - s2.x;
45418             var dy1 = s1.y - s2.y;
45419             var dx2 = s2.x - s3.x;
45420             var dy2 = s2.y - s3.y;
45421             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45422             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45423             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45424             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45425             var dxm = m1.x - m2.x;
45426             var dym = m1.y - m2.y;
45427             var k = l2 / (l1 + l2);
45428             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45429             var tx = s2.x - cm.x;
45430             var ty = s2.y - cm.y;
45431             return {
45432                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45433                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45434             };
45435         };
45436         Bezier.prototype.length = function () {
45437             var steps = 10;
45438             var length = 0;
45439             var px;
45440             var py;
45441             for (var i = 0; i <= steps; i += 1) {
45442                 var t = i / steps;
45443                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45444                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45445                 if (i > 0) {
45446                     var xdiff = cx - px;
45447                     var ydiff = cy - py;
45448                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45449                 }
45450                 px = cx;
45451                 py = cy;
45452             }
45453             return length;
45454         };
45455         Bezier.prototype.point = function (t, start, c1, c2, end) {
45456             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45457             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45458             + (3.0 * c2 * (1.0 - t) * t * t)
45459             + (end * t * t * t);
45460         };
45461         return Bezier;
45462     }()),
45463     
45464     throttleStroke: function(fn, wait) {
45465       if (wait === void 0) { wait = 250; }
45466       var previous = 0;
45467       var timeout = null;
45468       var result;
45469       var storedContext;
45470       var storedArgs;
45471       var later = function () {
45472           previous = Date.now();
45473           timeout = null;
45474           result = fn.apply(storedContext, storedArgs);
45475           if (!timeout) {
45476               storedContext = null;
45477               storedArgs = [];
45478           }
45479       };
45480       return function wrapper() {
45481           var args = [];
45482           for (var _i = 0; _i < arguments.length; _i++) {
45483               args[_i] = arguments[_i];
45484           }
45485           var now = Date.now();
45486           var remaining = wait - (now - previous);
45487           storedContext = this;
45488           storedArgs = args;
45489           if (remaining <= 0 || remaining > wait) {
45490               if (timeout) {
45491                   clearTimeout(timeout);
45492                   timeout = null;
45493               }
45494               previous = now;
45495               result = fn.apply(storedContext, storedArgs);
45496               if (!timeout) {
45497                   storedContext = null;
45498                   storedArgs = [];
45499               }
45500           }
45501           else if (!timeout) {
45502               timeout = window.setTimeout(later, remaining);
45503           }
45504           return result;
45505       };
45506   }
45507   
45508 });
45509
45510  
45511
45512