roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * @abstract
227  * @children Roo.bootstrap.Component
228  * Bootstrap Component base class
229  * @cfg {String} cls css class
230  * @cfg {String} style any extra css
231  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
233  * @cfg {string} dataId cutomer id
234  * @cfg {string} name Specifies name attribute
235  * @cfg {string} tooltip  Text for the tooltip
236  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
237  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
238  
239  * @constructor
240  * Do not use directly - it does not do anything..
241  * @param {Object} config The config object
242  */
243
244
245
246 Roo.bootstrap.Component = function(config){
247     Roo.bootstrap.Component.superclass.constructor.call(this, config);
248        
249     this.addEvents({
250         /**
251          * @event childrenrendered
252          * Fires when the children have been rendered..
253          * @param {Roo.bootstrap.Component} this
254          */
255         "childrenrendered" : true
256         
257         
258         
259     });
260     
261     
262 };
263
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
265     
266     
267     allowDomMove : false, // to stop relocations in parent onRender...
268     
269     cls : false,
270     
271     style : false,
272     
273     autoCreate : false,
274     
275     tooltip : null,
276     /**
277      * Initialize Events for the element
278      */
279     initEvents : function() { },
280     
281     xattr : false,
282     
283     parentId : false,
284     
285     can_build_overlaid : true,
286     
287     container_method : false,
288     
289     dataId : false,
290     
291     name : false,
292     
293     parent: function() {
294         // returns the parent component..
295         return Roo.ComponentMgr.get(this.parentId)
296         
297         
298     },
299     
300     // private
301     onRender : function(ct, position)
302     {
303        // Roo.log("Call onRender: " + this.xtype);
304         
305         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
306         
307         if(this.el){
308             if (this.el.attr('xtype')) {
309                 this.el.attr('xtypex', this.el.attr('xtype'));
310                 this.el.dom.removeAttribute('xtype');
311                 
312                 this.initEvents();
313             }
314             
315             return;
316         }
317         
318          
319         
320         var cfg = Roo.apply({},  this.getAutoCreate());
321         
322         cfg.id = this.id || Roo.id();
323         
324         // fill in the extra attributes 
325         if (this.xattr && typeof(this.xattr) =='object') {
326             for (var i in this.xattr) {
327                 cfg[i] = this.xattr[i];
328             }
329         }
330         
331         if(this.dataId){
332             cfg.dataId = this.dataId;
333         }
334         
335         if (this.cls) {
336             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
337         }
338         
339         if (this.style) { // fixme needs to support more complex style data.
340             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341         }
342         
343         if(this.name){
344             cfg.name = this.name;
345         }
346         
347         this.el = ct.createChild(cfg, position);
348         
349         if (this.tooltip) {
350             this.tooltipEl().attr('tooltip', this.tooltip);
351         }
352         
353         if(this.tabIndex !== undefined){
354             this.el.dom.setAttribute('tabIndex', this.tabIndex);
355         }
356         
357         this.initEvents();
358         
359     },
360     /**
361      * Fetch the element to add children to
362      * @return {Roo.Element} defaults to this.el
363      */
364     getChildContainer : function()
365     {
366         return this.el;
367     },
368     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
369     {
370         return Roo.get(document.body);
371     },
372     
373     /**
374      * Fetch the element to display the tooltip on.
375      * @return {Roo.Element} defaults to this.el
376      */
377     tooltipEl : function()
378     {
379         return this.el;
380     },
381         
382     addxtype  : function(tree,cntr)
383     {
384         var cn = this;
385         
386         cn = Roo.factory(tree);
387         //Roo.log(['addxtype', cn]);
388            
389         cn.parentType = this.xtype; //??
390         cn.parentId = this.id;
391         
392         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393         if (typeof(cn.container_method) == 'string') {
394             cntr = cn.container_method;
395         }
396         
397         
398         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
399         
400         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
401         
402         var build_from_html =  Roo.XComponent.build_from_html;
403           
404         var is_body  = (tree.xtype == 'Body') ;
405           
406         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
407           
408         var self_cntr_el = Roo.get(this[cntr](false));
409         
410         // do not try and build conditional elements 
411         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412             return false;
413         }
414         
415         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417                 return this.addxtypeChild(tree,cntr, is_body);
418             }
419             
420             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
421                 
422             if(echild){
423                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
424             }
425             
426             Roo.log('skipping render');
427             return cn;
428             
429         }
430         
431         var ret = false;
432         if (!build_from_html) {
433             return false;
434         }
435         
436         // this i think handles overlaying multiple children of the same type
437         // with the sam eelement.. - which might be buggy..
438         while (true) {
439             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
440             
441             if (!echild) {
442                 break;
443             }
444             
445             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446                 break;
447             }
448             
449             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
450         }
451        
452         return ret;
453     },
454     
455     
456     addxtypeChild : function (tree, cntr, is_body)
457     {
458         Roo.debug && Roo.log('addxtypeChild:' + cntr);
459         var cn = this;
460         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
461         
462         
463         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464                     (typeof(tree['flexy:foreach']) != 'undefined');
465           
466     
467         
468         skip_children = false;
469         // render the element if it's not BODY.
470         if (!is_body) {
471             
472             // if parent was disabled, then do not try and create the children..
473             if(!this[cntr](true)){
474                 tree.items = [];
475                 return tree;
476             }
477            
478             cn = Roo.factory(tree);
479            
480             cn.parentType = this.xtype; //??
481             cn.parentId = this.id;
482             
483             var build_from_html =  Roo.XComponent.build_from_html;
484             
485             
486             // does the container contain child eleemnts with 'xtype' attributes.
487             // that match this xtype..
488             // note - when we render we create these as well..
489             // so we should check to see if body has xtype set.
490             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
491                
492                 var self_cntr_el = Roo.get(this[cntr](false));
493                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
494                 if (echild) { 
495                     //Roo.log(Roo.XComponent.build_from_html);
496                     //Roo.log("got echild:");
497                     //Roo.log(echild);
498                 }
499                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500                 // and are not displayed -this causes this to use up the wrong element when matching.
501                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
502                 
503                 
504                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
506                   
507                   
508                   
509                     cn.el = echild;
510                   //  Roo.log("GOT");
511                     //echild.dom.removeAttribute('xtype');
512                 } else {
513                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514                     Roo.debug && Roo.log(self_cntr_el);
515                     Roo.debug && Roo.log(echild);
516                     Roo.debug && Roo.log(cn);
517                 }
518             }
519            
520             
521            
522             // if object has flexy:if - then it may or may not be rendered.
523             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
524                 // skip a flexy if element.
525                 Roo.debug && Roo.log('skipping render');
526                 Roo.debug && Roo.log(tree);
527                 if (!cn.el) {
528                     Roo.debug && Roo.log('skipping all children');
529                     skip_children = true;
530                 }
531                 
532              } else {
533                  
534                 // actually if flexy:foreach is found, we really want to create 
535                 // multiple copies here...
536                 //Roo.log('render');
537                 //Roo.log(this[cntr]());
538                 // some elements do not have render methods.. like the layouts...
539                 /*
540                 if(this[cntr](true) === false){
541                     cn.items = [];
542                     return cn;
543                 }
544                 */
545                 cn.render && cn.render(this[cntr](true));
546                 
547              }
548             // then add the element..
549         }
550          
551         // handle the kids..
552         
553         var nitems = [];
554         /*
555         if (typeof (tree.menu) != 'undefined') {
556             tree.menu.parentType = cn.xtype;
557             tree.menu.triggerEl = cn.el;
558             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559             
560         }
561         */
562         if (!tree.items || !tree.items.length) {
563             cn.items = nitems;
564             //Roo.log(["no children", this]);
565             
566             return cn;
567         }
568          
569         var items = tree.items;
570         delete tree.items;
571         
572         //Roo.log(items.length);
573             // add the items..
574         if (!skip_children) {    
575             for(var i =0;i < items.length;i++) {
576               //  Roo.log(['add child', items[i]]);
577                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
578             }
579         }
580         
581         cn.items = nitems;
582         
583         //Roo.log("fire childrenrendered");
584         
585         cn.fireEvent('childrenrendered', this);
586         
587         return cn;
588     },
589     
590     /**
591      * Set the element that will be used to show or hide
592      */
593     setVisibilityEl : function(el)
594     {
595         this.visibilityEl = el;
596     },
597     
598      /**
599      * Get the element that will be used to show or hide
600      */
601     getVisibilityEl : function()
602     {
603         if (typeof(this.visibilityEl) == 'object') {
604             return this.visibilityEl;
605         }
606         
607         if (typeof(this.visibilityEl) == 'string') {
608             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
609         }
610         
611         return this.getEl();
612     },
613     
614     /**
615      * Show a component - removes 'hidden' class
616      */
617     show : function()
618     {
619         if(!this.getVisibilityEl()){
620             return;
621         }
622          
623         this.getVisibilityEl().removeClass(['hidden','d-none']);
624         
625         this.fireEvent('show', this);
626         
627         
628     },
629     /**
630      * Hide a component - adds 'hidden' class
631      */
632     hide: function()
633     {
634         if(!this.getVisibilityEl()){
635             return;
636         }
637         
638         this.getVisibilityEl().addClass(['hidden','d-none']);
639         
640         this.fireEvent('hide', this);
641         
642     }
643 });
644
645  /*
646  * - LGPL
647  *
648  * element
649  * 
650  */
651
652 /**
653  * @class Roo.bootstrap.Element
654  * @extends Roo.bootstrap.Component
655  * Bootstrap Element class
656  * @cfg {String} html contents of the element
657  * @cfg {String} tag tag of the element
658  * @cfg {String} cls class of the element
659  * @cfg {Boolean} preventDefault (true|false) default false
660  * @cfg {Boolean} clickable (true|false) default false
661  * @cfg {String} role default blank - set to button to force cursor pointer
662  
663  * 
664  * @constructor
665  * Create a new Element
666  * @param {Object} config The config object
667  */
668
669 Roo.bootstrap.Element = function(config){
670     Roo.bootstrap.Element.superclass.constructor.call(this, config);
671     
672     this.addEvents({
673         // raw events
674         /**
675          * @event click
676          * When a element is chick
677          * @param {Roo.bootstrap.Element} this
678          * @param {Roo.EventObject} e
679          */
680         "click" : true 
681         
682       
683     });
684 };
685
686 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
687     
688     tag: 'div',
689     cls: '',
690     html: '',
691     preventDefault: false, 
692     clickable: false,
693     tapedTwice : false,
694     role : false,
695     
696     getAutoCreate : function(){
697         
698         var cfg = {
699             tag: this.tag,
700             // cls: this.cls, double assign in parent class Component.js :: onRender
701             html: this.html
702         };
703         if (this.role !== false) {
704             cfg.role = this.role;
705         }
706         
707         return cfg;
708     },
709     
710     initEvents: function() 
711     {
712         Roo.bootstrap.Element.superclass.initEvents.call(this);
713         
714         if(this.clickable){
715             this.el.on('click', this.onClick, this);
716         }
717         
718         
719     },
720     
721     onClick : function(e)
722     {
723         if(this.preventDefault){
724             e.preventDefault();
725         }
726         
727         this.fireEvent('click', this, e); // why was this double click before?
728     },
729     
730     
731     
732
733     
734     
735     getValue : function()
736     {
737         return this.el.dom.innerHTML;
738     },
739     
740     setValue : function(value)
741     {
742         this.el.dom.innerHTML = value;
743     }
744    
745 });
746
747  
748
749  /*
750  * - LGPL
751  *
752  * dropable area
753  * 
754  */
755
756 /**
757  * @class Roo.bootstrap.DropTarget
758  * @extends Roo.bootstrap.Element
759  * Bootstrap DropTarget class
760  
761  * @cfg {string} name dropable name
762  * 
763  * @constructor
764  * Create a new Dropable Area
765  * @param {Object} config The config object
766  */
767
768 Roo.bootstrap.DropTarget = function(config){
769     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
770     
771     this.addEvents({
772         // raw events
773         /**
774          * @event click
775          * When a element is chick
776          * @param {Roo.bootstrap.Element} this
777          * @param {Roo.EventObject} e
778          */
779         "drop" : true
780     });
781 };
782
783 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
784     
785     
786     getAutoCreate : function(){
787         
788          
789     },
790     
791     initEvents: function() 
792     {
793         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
794         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
795             ddGroup: this.name,
796             listeners : {
797                 drop : this.dragDrop.createDelegate(this),
798                 enter : this.dragEnter.createDelegate(this),
799                 out : this.dragOut.createDelegate(this),
800                 over : this.dragOver.createDelegate(this)
801             }
802             
803         });
804         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
805     },
806     
807     dragDrop : function(source,e,data)
808     {
809         // user has to decide how to impliment this.
810         Roo.log('drop');
811         Roo.log(this);
812         //this.fireEvent('drop', this, source, e ,data);
813         return false;
814     },
815     
816     dragEnter : function(n, dd, e, data)
817     {
818         // probably want to resize the element to match the dropped element..
819         Roo.log("enter");
820         this.originalSize = this.el.getSize();
821         this.el.setSize( n.el.getSize());
822         this.dropZone.DDM.refreshCache(this.name);
823         Roo.log([n, dd, e, data]);
824     },
825     
826     dragOut : function(value)
827     {
828         // resize back to normal
829         Roo.log("out");
830         this.el.setSize(this.originalSize);
831         this.dropZone.resetConstraints();
832     },
833     
834     dragOver : function()
835     {
836         // ??? do nothing?
837     }
838    
839 });
840
841  
842
843  /*
844  * - LGPL
845  *
846  * Body
847  *
848  */
849
850 /**
851  * @class Roo.bootstrap.Body
852  * @extends Roo.bootstrap.Component
853  * @builder-top
854  * @children Roo.bootstrap.Component
855  * @parent none
856  * Bootstrap Body class
857  *
858  * @constructor
859  * Create a new body
860  * @param {Object} config The config object
861  */
862
863 Roo.bootstrap.Body = function(config){
864
865     config = config || {};
866
867     Roo.bootstrap.Body.superclass.constructor.call(this, config);
868     this.el = Roo.get(config.el ? config.el : document.body );
869     if (this.cls && this.cls.length) {
870         Roo.get(document.body).addClass(this.cls);
871     }
872 };
873
874 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
875
876     is_body : true,// just to make sure it's constructed?
877
878         autoCreate : {
879         cls: 'container'
880     },
881     onRender : function(ct, position)
882     {
883        /* Roo.log("Roo.bootstrap.Body - onRender");
884         if (this.cls && this.cls.length) {
885             Roo.get(document.body).addClass(this.cls);
886         }
887         // style??? xttr???
888         */
889     }
890
891
892
893
894 });
895 /*
896  * - LGPL
897  *
898  * button group
899  * 
900  */
901
902
903 /**
904  * @class Roo.bootstrap.ButtonGroup
905  * @extends Roo.bootstrap.Component
906  * Bootstrap ButtonGroup class
907  * @cfg {String} size lg | sm | xs (default empty normal)
908  * @cfg {String} align vertical | justified  (default none)
909  * @cfg {String} direction up | down (default down)
910  * @cfg {Boolean} toolbar false | true
911  * @cfg {Boolean} btn true | false
912  * 
913  * 
914  * @constructor
915  * Create a new Input
916  * @param {Object} config The config object
917  */
918
919 Roo.bootstrap.ButtonGroup = function(config){
920     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
921 };
922
923 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
924     
925     size: '',
926     align: '',
927     direction: '',
928     toolbar: false,
929     btn: true,
930
931     getAutoCreate : function(){
932         var cfg = {
933             cls: 'btn-group',
934             html : null
935         };
936         
937         cfg.html = this.html || cfg.html;
938         
939         if (this.toolbar) {
940             cfg = {
941                 cls: 'btn-toolbar',
942                 html: null
943             };
944             
945             return cfg;
946         }
947         
948         if (['vertical','justified'].indexOf(this.align)!==-1) {
949             cfg.cls = 'btn-group-' + this.align;
950             
951             if (this.align == 'justified') {
952                 console.log(this.items);
953             }
954         }
955         
956         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
957             cfg.cls += ' btn-group-' + this.size;
958         }
959         
960         if (this.direction == 'up') {
961             cfg.cls += ' dropup' ;
962         }
963         
964         return cfg;
965     },
966     /**
967      * Add a button to the group (similar to NavItem API.)
968      */
969     addItem : function(cfg)
970     {
971         var cn = new Roo.bootstrap.Button(cfg);
972         //this.register(cn);
973         cn.parentId = this.id;
974         cn.onRender(this.el, null);
975         return cn;
976     }
977    
978 });
979
980  /*
981  * - LGPL
982  *
983  * button
984  * 
985  */
986
987 /**
988  * @class Roo.bootstrap.Button
989  * @extends Roo.bootstrap.Component
990  * Bootstrap Button class
991  * @cfg {String} html The button content
992  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
993  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
994  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
995  * @cfg {String} size (lg|sm|xs)
996  * @cfg {String} tag (a|input|submit)
997  * @cfg {String} href empty or href
998  * @cfg {Boolean} disabled default false;
999  * @cfg {Boolean} isClose default false;
1000  * @cfg {String} glyphicon depricated - use fa
1001  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1002  * @cfg {String} badge text for badge
1003  * @cfg {String} theme (default|glow)  
1004  * @cfg {Boolean} inverse dark themed version
1005  * @cfg {Boolean} toggle is it a slidy toggle button
1006  * @cfg {Boolean} pressed   default null - if the button ahs active state
1007  * @cfg {String} ontext text for on slidy toggle state
1008  * @cfg {String} offtext text for off slidy toggle state
1009  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1010  * @cfg {Boolean} removeClass remove the standard class..
1011  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1012  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1013  * 
1014  * @constructor
1015  * Create a new button
1016  * @param {Object} config The config object
1017  */
1018
1019
1020 Roo.bootstrap.Button = function(config){
1021     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1022     
1023     this.addEvents({
1024         // raw events
1025         /**
1026          * @event click
1027          * When a button is pressed
1028          * @param {Roo.bootstrap.Button} btn
1029          * @param {Roo.EventObject} e
1030          */
1031         "click" : true,
1032         /**
1033          * @event dblclick
1034          * When a button is double clicked
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "dblclick" : true,
1039          /**
1040          * @event toggle
1041          * After the button has been toggles
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          * @param {boolean} pressed (also available as button.pressed)
1045          */
1046         "toggle" : true
1047     });
1048 };
1049
1050 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1051     html: false,
1052     active: false,
1053     weight: '',
1054     badge_weight: '',
1055     outline : false,
1056     size: '',
1057     tag: 'button',
1058     href: '',
1059     disabled: false,
1060     isClose: false,
1061     glyphicon: '',
1062     fa: '',
1063     badge: '',
1064     theme: 'default',
1065     inverse: false,
1066     
1067     toggle: false,
1068     ontext: 'ON',
1069     offtext: 'OFF',
1070     defaulton: true,
1071     preventDefault: true,
1072     removeClass: false,
1073     name: false,
1074     target: false,
1075     group : false,
1076      
1077     pressed : null,
1078      
1079     
1080     getAutoCreate : function(){
1081         
1082         var cfg = {
1083             tag : 'button',
1084             cls : 'roo-button',
1085             html: ''
1086         };
1087         
1088         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1089             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1090             this.tag = 'button';
1091         } else {
1092             cfg.tag = this.tag;
1093         }
1094         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1095         
1096         if (this.toggle == true) {
1097             cfg={
1098                 tag: 'div',
1099                 cls: 'slider-frame roo-button',
1100                 cn: [
1101                     {
1102                         tag: 'span',
1103                         'data-on-text':'ON',
1104                         'data-off-text':'OFF',
1105                         cls: 'slider-button',
1106                         html: this.offtext
1107                     }
1108                 ]
1109             };
1110             // why are we validating the weights?
1111             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1112                 cfg.cls +=  ' ' + this.weight;
1113             }
1114             
1115             return cfg;
1116         }
1117         
1118         if (this.isClose) {
1119             cfg.cls += ' close';
1120             
1121             cfg["aria-hidden"] = true;
1122             
1123             cfg.html = "&times;";
1124             
1125             return cfg;
1126         }
1127              
1128         
1129         if (this.theme==='default') {
1130             cfg.cls = 'btn roo-button';
1131             
1132             //if (this.parentType != 'Navbar') {
1133             this.weight = this.weight.length ?  this.weight : 'default';
1134             //}
1135             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1136                 
1137                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1138                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1139                 cfg.cls += ' btn-' + outline + weight;
1140                 if (this.weight == 'default') {
1141                     // BC
1142                     cfg.cls += ' btn-' + this.weight;
1143                 }
1144             }
1145         } else if (this.theme==='glow') {
1146             
1147             cfg.tag = 'a';
1148             cfg.cls = 'btn-glow roo-button';
1149             
1150             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1151                 
1152                 cfg.cls += ' ' + this.weight;
1153             }
1154         }
1155    
1156         
1157         if (this.inverse) {
1158             this.cls += ' inverse';
1159         }
1160         
1161         
1162         if (this.active || this.pressed === true) {
1163             cfg.cls += ' active';
1164         }
1165         
1166         if (this.disabled) {
1167             cfg.disabled = 'disabled';
1168         }
1169         
1170         if (this.items) {
1171             Roo.log('changing to ul' );
1172             cfg.tag = 'ul';
1173             this.glyphicon = 'caret';
1174             if (Roo.bootstrap.version == 4) {
1175                 this.fa = 'caret-down';
1176             }
1177             
1178         }
1179         
1180         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1181          
1182         //gsRoo.log(this.parentType);
1183         if (this.parentType === 'Navbar' && !this.parent().bar) {
1184             Roo.log('changing to li?');
1185             
1186             cfg.tag = 'li';
1187             
1188             cfg.cls = '';
1189             cfg.cn =  [{
1190                 tag : 'a',
1191                 cls : 'roo-button',
1192                 html : this.html,
1193                 href : this.href || '#'
1194             }];
1195             if (this.menu) {
1196                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1197                 cfg.cls += ' dropdown';
1198             }   
1199             
1200             delete cfg.html;
1201             
1202         }
1203         
1204        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1205         
1206         if (this.glyphicon) {
1207             cfg.html = ' ' + cfg.html;
1208             
1209             cfg.cn = [
1210                 {
1211                     tag: 'span',
1212                     cls: 'glyphicon glyphicon-' + this.glyphicon
1213                 }
1214             ];
1215         }
1216         if (this.fa) {
1217             cfg.html = ' ' + cfg.html;
1218             
1219             cfg.cn = [
1220                 {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa
1223                 }
1224             ];
1225         }
1226         
1227         if (this.badge) {
1228             cfg.html += ' ';
1229             
1230             cfg.tag = 'a';
1231             
1232 //            cfg.cls='btn roo-button';
1233             
1234             cfg.href=this.href;
1235             
1236             var value = cfg.html;
1237             
1238             if(this.glyphicon){
1239                 value = {
1240                     tag: 'span',
1241                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1242                     html: this.html
1243                 };
1244             }
1245             if(this.fa){
1246                 value = {
1247                     tag: 'i',
1248                     cls: 'fa fas fa-' + this.fa,
1249                     html: this.html
1250                 };
1251             }
1252             
1253             var bw = this.badge_weight.length ? this.badge_weight :
1254                 (this.weight.length ? this.weight : 'secondary');
1255             bw = bw == 'default' ? 'secondary' : bw;
1256             
1257             cfg.cn = [
1258                 value,
1259                 {
1260                     tag: 'span',
1261                     cls: 'badge badge-' + bw,
1262                     html: this.badge
1263                 }
1264             ];
1265             
1266             cfg.html='';
1267         }
1268         
1269         if (this.menu) {
1270             cfg.cls += ' dropdown';
1271             cfg.html = typeof(cfg.html) != 'undefined' ?
1272                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1273         }
1274         
1275         if (cfg.tag !== 'a' && this.href !== '') {
1276             throw "Tag must be a to set href.";
1277         } else if (this.href.length > 0) {
1278             cfg.href = this.href;
1279         }
1280         
1281         if(this.removeClass){
1282             cfg.cls = '';
1283         }
1284         
1285         if(this.target){
1286             cfg.target = this.target;
1287         }
1288         
1289         return cfg;
1290     },
1291     initEvents: function() {
1292        // Roo.log('init events?');
1293 //        Roo.log(this.el.dom);
1294         // add the menu...
1295         
1296         if (typeof (this.menu) != 'undefined') {
1297             this.menu.parentType = this.xtype;
1298             this.menu.triggerEl = this.el;
1299             this.addxtype(Roo.apply({}, this.menu));
1300         }
1301
1302
1303         if (this.el.hasClass('roo-button')) {
1304              this.el.on('click', this.onClick, this);
1305              this.el.on('dblclick', this.onDblClick, this);
1306         } else {
1307              this.el.select('.roo-button').on('click', this.onClick, this);
1308              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1309              
1310         }
1311         // why?
1312         if(this.removeClass){
1313             this.el.on('click', this.onClick, this);
1314         }
1315         
1316         if (this.group === true) {
1317              if (this.pressed === false || this.pressed === true) {
1318                 // nothing
1319             } else {
1320                 this.pressed = false;
1321                 this.setActive(this.pressed);
1322             }
1323             
1324         }
1325         
1326         this.el.enableDisplayMode();
1327         
1328     },
1329     onClick : function(e)
1330     {
1331         if (this.disabled) {
1332             return;
1333         }
1334         
1335         Roo.log('button on click ');
1336         if(this.preventDefault){
1337             e.preventDefault();
1338         }
1339         
1340         if (this.group) {
1341             if (this.pressed) {
1342                 // do nothing -
1343                 return;
1344             }
1345             this.setActive(true);
1346             var pi = this.parent().items;
1347             for (var i = 0;i < pi.length;i++) {
1348                 if (this == pi[i]) {
1349                     continue;
1350                 }
1351                 if (pi[i].el.hasClass('roo-button')) {
1352                     pi[i].setActive(false);
1353                 }
1354             }
1355             this.fireEvent('click', this, e);            
1356             return;
1357         }
1358         
1359         if (this.pressed === true || this.pressed === false) {
1360             this.toggleActive(e);
1361         }
1362         
1363         
1364         this.fireEvent('click', this, e);
1365     },
1366     onDblClick: function(e)
1367     {
1368         if (this.disabled) {
1369             return;
1370         }
1371         if(this.preventDefault){
1372             e.preventDefault();
1373         }
1374         this.fireEvent('dblclick', this, e);
1375     },
1376     /**
1377      * Enables this button
1378      */
1379     enable : function()
1380     {
1381         this.disabled = false;
1382         this.el.removeClass('disabled');
1383         this.el.dom.removeAttribute("disabled");
1384     },
1385     
1386     /**
1387      * Disable this button
1388      */
1389     disable : function()
1390     {
1391         this.disabled = true;
1392         this.el.addClass('disabled');
1393         this.el.attr("disabled", "disabled")
1394     },
1395      /**
1396      * sets the active state on/off, 
1397      * @param {Boolean} state (optional) Force a particular state
1398      */
1399     setActive : function(v) {
1400         
1401         this.el[v ? 'addClass' : 'removeClass']('active');
1402         this.pressed = v;
1403     },
1404      /**
1405      * toggles the current active state 
1406      */
1407     toggleActive : function(e)
1408     {
1409         this.setActive(!this.pressed); // this modifies pressed...
1410         this.fireEvent('toggle', this, e, this.pressed);
1411     },
1412      /**
1413      * get the current active state
1414      * @return {boolean} true if it's active
1415      */
1416     isActive : function()
1417     {
1418         return this.el.hasClass('active');
1419     },
1420     /**
1421      * set the text of the first selected button
1422      */
1423     setText : function(str)
1424     {
1425         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1426     },
1427     /**
1428      * get the text of the first selected button
1429      */
1430     getText : function()
1431     {
1432         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1433     },
1434     
1435     setWeight : function(str)
1436     {
1437         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1438         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1439         this.weight = str;
1440         var outline = this.outline ? 'outline-' : '';
1441         if (str == 'default') {
1442             this.el.addClass('btn-default btn-outline-secondary');        
1443             return;
1444         }
1445         this.el.addClass('btn-' + outline + str);        
1446     }
1447     
1448     
1449 });
1450 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1451
1452 Roo.bootstrap.Button.weights = [
1453     'default',
1454     'secondary' ,
1455     'primary',
1456     'success',
1457     'info',
1458     'warning',
1459     'danger',
1460     'link',
1461     'light',
1462     'dark'              
1463    
1464 ];/*
1465  * - LGPL
1466  *
1467  * column
1468  * 
1469  */
1470
1471 /**
1472  * @class Roo.bootstrap.Column
1473  * @extends Roo.bootstrap.Component
1474  * @children Roo.bootstrap.Component
1475  * Bootstrap Column class
1476  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1477  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1478  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1479  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1480  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1481  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1482  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1483  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1484  *
1485  * 
1486  * @cfg {Boolean} hidden (true|false) hide the element
1487  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1488  * @cfg {String} fa (ban|check|...) font awesome icon
1489  * @cfg {Number} fasize (1|2|....) font awsome size
1490
1491  * @cfg {String} icon (info-sign|check|...) glyphicon name
1492
1493  * @cfg {String} html content of column.
1494  * 
1495  * @constructor
1496  * Create a new Column
1497  * @param {Object} config The config object
1498  */
1499
1500 Roo.bootstrap.Column = function(config){
1501     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1502 };
1503
1504 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1505     
1506     xs: false,
1507     sm: false,
1508     md: false,
1509     lg: false,
1510     xsoff: false,
1511     smoff: false,
1512     mdoff: false,
1513     lgoff: false,
1514     html: '',
1515     offset: 0,
1516     alert: false,
1517     fa: false,
1518     icon : false,
1519     hidden : false,
1520     fasize : 1,
1521     
1522     getAutoCreate : function(){
1523         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524         
1525         cfg = {
1526             tag: 'div',
1527             cls: 'column'
1528         };
1529         
1530         var settings=this;
1531         var sizes =   ['xs','sm','md','lg'];
1532         sizes.map(function(size ,ix){
1533             //Roo.log( size + ':' + settings[size]);
1534             
1535             if (settings[size+'off'] !== false) {
1536                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1537             }
1538             
1539             if (settings[size] === false) {
1540                 return;
1541             }
1542             
1543             if (!settings[size]) { // 0 = hidden
1544                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1545                 // bootsrap4
1546                 for (var i = ix; i > -1; i--) {
1547                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1548                 }
1549                 
1550                 
1551                 return;
1552             }
1553             cfg.cls += ' col-' + size + '-' + settings[size] + (
1554                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1555             );
1556             
1557         });
1558         
1559         if (this.hidden) {
1560             cfg.cls += ' hidden';
1561         }
1562         
1563         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1564             cfg.cls +=' alert alert-' + this.alert;
1565         }
1566         
1567         
1568         if (this.html.length) {
1569             cfg.html = this.html;
1570         }
1571         if (this.fa) {
1572             var fasize = '';
1573             if (this.fasize > 1) {
1574                 fasize = ' fa-' + this.fasize + 'x';
1575             }
1576             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1577             
1578             
1579         }
1580         if (this.icon) {
1581             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1582         }
1583         
1584         return cfg;
1585     }
1586    
1587 });
1588
1589  
1590
1591  /*
1592  * - LGPL
1593  *
1594  * page container.
1595  * 
1596  */
1597
1598
1599 /**
1600  * @class Roo.bootstrap.Container
1601  * @extends Roo.bootstrap.Component
1602  * @builder-top
1603  * @children Roo.bootstrap.Component
1604  * Bootstrap Container class
1605  * @cfg {Boolean} jumbotron is it a jumbotron element
1606  * @cfg {String} html content of element
1607  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1608  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1609  * @cfg {String} header content of header (for panel)
1610  * @cfg {String} footer content of footer (for panel)
1611  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1612  * @cfg {String} tag (header|aside|section) type of HTML tag.
1613  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1614  * @cfg {String} fa font awesome icon
1615  * @cfg {String} icon (info-sign|check|...) glyphicon name
1616  * @cfg {Boolean} hidden (true|false) hide the element
1617  * @cfg {Boolean} expandable (true|false) default false
1618  * @cfg {Boolean} expanded (true|false) default true
1619  * @cfg {String} rheader contet on the right of header
1620  * @cfg {Boolean} clickable (true|false) default false
1621
1622  *     
1623  * @constructor
1624  * Create a new Container
1625  * @param {Object} config The config object
1626  */
1627
1628 Roo.bootstrap.Container = function(config){
1629     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1630     
1631     this.addEvents({
1632         // raw events
1633          /**
1634          * @event expand
1635          * After the panel has been expand
1636          * 
1637          * @param {Roo.bootstrap.Container} this
1638          */
1639         "expand" : true,
1640         /**
1641          * @event collapse
1642          * After the panel has been collapsed
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "collapse" : true,
1647         /**
1648          * @event click
1649          * When a element is chick
1650          * @param {Roo.bootstrap.Container} this
1651          * @param {Roo.EventObject} e
1652          */
1653         "click" : true
1654     });
1655 };
1656
1657 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1658     
1659     jumbotron : false,
1660     well: '',
1661     panel : '',
1662     header: '',
1663     footer : '',
1664     sticky: '',
1665     tag : false,
1666     alert : false,
1667     fa: false,
1668     icon : false,
1669     expandable : false,
1670     rheader : '',
1671     expanded : true,
1672     clickable: false,
1673   
1674      
1675     getChildContainer : function() {
1676         
1677         if(!this.el){
1678             return false;
1679         }
1680         
1681         if (this.panel.length) {
1682             return this.el.select('.panel-body',true).first();
1683         }
1684         
1685         return this.el;
1686     },
1687     
1688     
1689     getAutoCreate : function(){
1690         
1691         var cfg = {
1692             tag : this.tag || 'div',
1693             html : '',
1694             cls : ''
1695         };
1696         if (this.jumbotron) {
1697             cfg.cls = 'jumbotron';
1698         }
1699         
1700         
1701         
1702         // - this is applied by the parent..
1703         //if (this.cls) {
1704         //    cfg.cls = this.cls + '';
1705         //}
1706         
1707         if (this.sticky.length) {
1708             
1709             var bd = Roo.get(document.body);
1710             if (!bd.hasClass('bootstrap-sticky')) {
1711                 bd.addClass('bootstrap-sticky');
1712                 Roo.select('html',true).setStyle('height', '100%');
1713             }
1714              
1715             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1716         }
1717         
1718         
1719         if (this.well.length) {
1720             switch (this.well) {
1721                 case 'lg':
1722                 case 'sm':
1723                     cfg.cls +=' well well-' +this.well;
1724                     break;
1725                 default:
1726                     cfg.cls +=' well';
1727                     break;
1728             }
1729         }
1730         
1731         if (this.hidden) {
1732             cfg.cls += ' hidden';
1733         }
1734         
1735         
1736         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1737             cfg.cls +=' alert alert-' + this.alert;
1738         }
1739         
1740         var body = cfg;
1741         
1742         if (this.panel.length) {
1743             cfg.cls += ' panel panel-' + this.panel;
1744             cfg.cn = [];
1745             if (this.header.length) {
1746                 
1747                 var h = [];
1748                 
1749                 if(this.expandable){
1750                     
1751                     cfg.cls = cfg.cls + ' expandable';
1752                     
1753                     h.push({
1754                         tag: 'i',
1755                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1756                     });
1757                     
1758                 }
1759                 
1760                 h.push(
1761                     {
1762                         tag: 'span',
1763                         cls : 'panel-title',
1764                         html : (this.expandable ? '&nbsp;' : '') + this.header
1765                     },
1766                     {
1767                         tag: 'span',
1768                         cls: 'panel-header-right',
1769                         html: this.rheader
1770                     }
1771                 );
1772                 
1773                 cfg.cn.push({
1774                     cls : 'panel-heading',
1775                     style : this.expandable ? 'cursor: pointer' : '',
1776                     cn : h
1777                 });
1778                 
1779             }
1780             
1781             body = false;
1782             cfg.cn.push({
1783                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1784                 html : this.html
1785             });
1786             
1787             
1788             if (this.footer.length) {
1789                 cfg.cn.push({
1790                     cls : 'panel-footer',
1791                     html : this.footer
1792                     
1793                 });
1794             }
1795             
1796         }
1797         
1798         if (body) {
1799             body.html = this.html || cfg.html;
1800             // prefix with the icons..
1801             if (this.fa) {
1802                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1803             }
1804             if (this.icon) {
1805                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1806             }
1807             
1808             
1809         }
1810         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1811             cfg.cls =  'container';
1812         }
1813         
1814         return cfg;
1815     },
1816     
1817     initEvents: function() 
1818     {
1819         if(this.expandable){
1820             var headerEl = this.headerEl();
1821         
1822             if(headerEl){
1823                 headerEl.on('click', this.onToggleClick, this);
1824             }
1825         }
1826         
1827         if(this.clickable){
1828             this.el.on('click', this.onClick, this);
1829         }
1830         
1831     },
1832     
1833     onToggleClick : function()
1834     {
1835         var headerEl = this.headerEl();
1836         
1837         if(!headerEl){
1838             return;
1839         }
1840         
1841         if(this.expanded){
1842             this.collapse();
1843             return;
1844         }
1845         
1846         this.expand();
1847     },
1848     
1849     expand : function()
1850     {
1851         if(this.fireEvent('expand', this)) {
1852             
1853             this.expanded = true;
1854             
1855             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1856             
1857             this.el.select('.panel-body',true).first().removeClass('hide');
1858             
1859             var toggleEl = this.toggleEl();
1860
1861             if(!toggleEl){
1862                 return;
1863             }
1864
1865             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1866         }
1867         
1868     },
1869     
1870     collapse : function()
1871     {
1872         if(this.fireEvent('collapse', this)) {
1873             
1874             this.expanded = false;
1875             
1876             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1877             this.el.select('.panel-body',true).first().addClass('hide');
1878         
1879             var toggleEl = this.toggleEl();
1880
1881             if(!toggleEl){
1882                 return;
1883             }
1884
1885             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1886         }
1887     },
1888     
1889     toggleEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading .fa',true).first();
1896     },
1897     
1898     headerEl : function()
1899     {
1900         if(!this.el || !this.panel.length || !this.header.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-heading',true).first()
1905     },
1906     
1907     bodyEl : function()
1908     {
1909         if(!this.el || !this.panel.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-body',true).first()
1914     },
1915     
1916     titleEl : function()
1917     {
1918         if(!this.el || !this.panel.length || !this.header.length){
1919             return;
1920         }
1921         
1922         return this.el.select('.panel-title',true).first();
1923     },
1924     
1925     setTitle : function(v)
1926     {
1927         var titleEl = this.titleEl();
1928         
1929         if(!titleEl){
1930             return;
1931         }
1932         
1933         titleEl.dom.innerHTML = v;
1934     },
1935     
1936     getTitle : function()
1937     {
1938         
1939         var titleEl = this.titleEl();
1940         
1941         if(!titleEl){
1942             return '';
1943         }
1944         
1945         return titleEl.dom.innerHTML;
1946     },
1947     
1948     setRightTitle : function(v)
1949     {
1950         var t = this.el.select('.panel-header-right',true).first();
1951         
1952         if(!t){
1953             return;
1954         }
1955         
1956         t.dom.innerHTML = v;
1957     },
1958     
1959     onClick : function(e)
1960     {
1961         e.preventDefault();
1962         
1963         this.fireEvent('click', this, e);
1964     }
1965 });
1966
1967  /*
1968  *  - LGPL
1969  *
1970  *  This is BS4's Card element.. - similar to our containers probably..
1971  * 
1972  */
1973 /**
1974  * @class Roo.bootstrap.Card
1975  * @extends Roo.bootstrap.Component
1976  * @children Roo.bootstrap.Component
1977  * Bootstrap Card class
1978  *
1979  *
1980  * possible... may not be implemented..
1981  * @cfg {String} header_image  src url of image.
1982  * @cfg {String|Object} header
1983  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1984  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1985  * 
1986  * @cfg {String} title
1987  * @cfg {String} subtitle
1988  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1989  * @cfg {String} footer
1990  
1991  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1992  * 
1993  * @cfg {String} margin (0|1|2|3|4|5|auto)
1994  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1995  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2000  *
2001  * @cfg {String} padding (0|1|2|3|4|5)
2002  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2003  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2004  * @cfg {String} padding_left (0|1|2|3|4|5)
2005  * @cfg {String} padding_right (0|1|2|3|4|5)
2006  * @cfg {String} padding_x (0|1|2|3|4|5)
2007  * @cfg {String} padding_y (0|1|2|3|4|5)
2008  *
2009  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2011  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  
2015  * @config {Boolean} dragable  if this card can be dragged.
2016  * @config {String} drag_group  group for drag
2017  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2018  * @config {String} drop_group  group for drag
2019  * 
2020  * @config {Boolean} collapsable can the body be collapsed.
2021  * @config {Boolean} collapsed is the body collapsed when rendered...
2022  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2023  * @config {Boolean} rotated is the body rotated when rendered...
2024  * 
2025  * @constructor
2026  * Create a new Container
2027  * @param {Object} config The config object
2028  */
2029
2030 Roo.bootstrap.Card = function(config){
2031     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2032     
2033     this.addEvents({
2034          // raw events
2035         /**
2036          * @event drop
2037          * When a element a card is dropped
2038          * @param {Roo.bootstrap.Card} this
2039          *
2040          * 
2041          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2042          * @param {String} position 'above' or 'below'
2043          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2044         
2045          */
2046         'drop' : true,
2047          /**
2048          * @event rotate
2049          * When a element a card is rotate
2050          * @param {Roo.bootstrap.Card} this
2051          * @param {Roo.Element} n the node being dropped?
2052          * @param {Boolean} rotate status
2053          */
2054         'rotate' : true,
2055         /**
2056          * @event cardover
2057          * When a card element is dragged over ready to drop (return false to block dropable)
2058          * @param {Roo.bootstrap.Card} this
2059          * @param {Object} data from dragdrop 
2060          */
2061          'cardover' : true
2062          
2063     });
2064 };
2065
2066
2067 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2068     
2069     
2070     weight : '',
2071     
2072     margin: '', /// may be better in component?
2073     margin_top: '', 
2074     margin_bottom: '', 
2075     margin_left: '',
2076     margin_right: '',
2077     margin_x: '',
2078     margin_y: '',
2079     
2080     padding : '',
2081     padding_top: '', 
2082     padding_bottom: '', 
2083     padding_left: '',
2084     padding_right: '',
2085     padding_x: '',
2086     padding_y: '',
2087     
2088     display: '', 
2089     display_xs: '', 
2090     display_sm: '', 
2091     display_lg: '',
2092     display_xl: '',
2093  
2094     header_image  : '',
2095     header : '',
2096     header_size : 0,
2097     title : '',
2098     subtitle : '',
2099     html : '',
2100     footer: '',
2101
2102     collapsable : false,
2103     collapsed : false,
2104     rotateable : false,
2105     rotated : false,
2106     
2107     dragable : false,
2108     drag_group : false,
2109     dropable : false,
2110     drop_group : false,
2111     childContainer : false,
2112     dropEl : false, /// the dom placeholde element that indicates drop location.
2113     containerEl: false, // body container
2114     bodyEl: false, // card-body
2115     headerContainerEl : false, //
2116     headerEl : false,
2117     header_imageEl : false,
2118     
2119     
2120     layoutCls : function()
2121     {
2122         var cls = '';
2123         var t = this;
2124         Roo.log(this.margin_bottom.length);
2125         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2126             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2127             
2128             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2130             }
2131             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2132                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2133             }
2134         });
2135         
2136         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2137             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2138                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2139             }
2140         });
2141         
2142         // more generic support?
2143         if (this.hidden) {
2144             cls += ' d-none';
2145         }
2146         
2147         return cls;
2148     },
2149  
2150        // Roo.log("Call onRender: " + this.xtype);
2151         /*  We are looking at something like this.
2152 <div class="card">
2153     <img src="..." class="card-img-top" alt="...">
2154     <div class="card-body">
2155         <h5 class="card-title">Card title</h5>
2156          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2157
2158         >> this bit is really the body...
2159         <div> << we will ad dthis in hopefully it will not break shit.
2160         
2161         ** card text does not actually have any styling...
2162         
2163             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2164         
2165         </div> <<
2166           <a href="#" class="card-link">Card link</a>
2167           
2168     </div>
2169     <div class="card-footer">
2170         <small class="text-muted">Last updated 3 mins ago</small>
2171     </div>
2172 </div>
2173          */
2174     getAutoCreate : function(){
2175         
2176         var cfg = {
2177             tag : 'div',
2178             cls : 'card',
2179             cn : [ ]
2180         };
2181         
2182         if (this.weight.length && this.weight != 'light') {
2183             cfg.cls += ' text-white';
2184         } else {
2185             cfg.cls += ' text-dark'; // need as it's nested..
2186         }
2187         if (this.weight.length) {
2188             cfg.cls += ' bg-' + this.weight;
2189         }
2190         
2191         cfg.cls += ' ' + this.layoutCls(); 
2192         
2193         var hdr = false;
2194         var hdr_ctr = false;
2195         if (this.header.length) {
2196             hdr = {
2197                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2198                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2199                 cn : []
2200             };
2201             cfg.cn.push(hdr);
2202             hdr_ctr = hdr;
2203         } else {
2204             hdr = {
2205                 tag : 'div',
2206                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2207                 cn : []
2208             };
2209             cfg.cn.push(hdr);
2210             hdr_ctr = hdr;
2211         }
2212         if (this.collapsable) {
2213             hdr_ctr = {
2214             tag : 'a',
2215             cls : 'd-block user-select-none',
2216             cn: [
2217                     {
2218                         tag: 'i',
2219                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2220                     }
2221                    
2222                 ]
2223             };
2224             hdr.cn.push(hdr_ctr);
2225         }
2226         
2227         hdr_ctr.cn.push(        {
2228             tag: 'span',
2229             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2230             html : this.header
2231         });
2232         
2233         
2234         if (this.header_image.length) {
2235             cfg.cn.push({
2236                 tag : 'img',
2237                 cls : 'card-img-top',
2238                 src: this.header_image // escape?
2239             });
2240         } else {
2241             cfg.cn.push({
2242                     tag : 'div',
2243                     cls : 'card-img-top d-none' 
2244                 });
2245         }
2246             
2247         var body = {
2248             tag : 'div',
2249             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2250             cn : []
2251         };
2252         var obody = body;
2253         if (this.collapsable || this.rotateable) {
2254             obody = {
2255                 tag: 'div',
2256                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2257                 cn : [  body ]
2258             };
2259         }
2260         
2261         cfg.cn.push(obody);
2262         
2263         if (this.title.length) {
2264             body.cn.push({
2265                 tag : 'div',
2266                 cls : 'card-title',
2267                 src: this.title // escape?
2268             });
2269         }  
2270         
2271         if (this.subtitle.length) {
2272             body.cn.push({
2273                 tag : 'div',
2274                 cls : 'card-title',
2275                 src: this.subtitle // escape?
2276             });
2277         }
2278         
2279         body.cn.push({
2280             tag : 'div',
2281             cls : 'roo-card-body-ctr'
2282         });
2283         
2284         if (this.html.length) {
2285             body.cn.push({
2286                 tag: 'div',
2287                 html : this.html
2288             });
2289         }
2290         // fixme ? handle objects?
2291         
2292         if (this.footer.length) {
2293            
2294             cfg.cn.push({
2295                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2296                 html : this.footer
2297             });
2298             
2299         } else {
2300             cfg.cn.push({cls : 'card-footer d-none'});
2301         }
2302         
2303         // footer...
2304         
2305         return cfg;
2306     },
2307     
2308     
2309     getCardHeader : function()
2310     {
2311         var  ret = this.el.select('.card-header',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardFooter : function()
2319     {
2320         var  ret = this.el.select('.card-footer',true).first();
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324         
2325         return ret;
2326     },
2327     getCardImageTop : function()
2328     {
2329         var  ret = this.header_imageEl;
2330         if (ret.hasClass('d-none')) {
2331             ret.removeClass('d-none');
2332         }
2333             
2334         return ret;
2335     },
2336     
2337     getChildContainer : function()
2338     {
2339         
2340         if(!this.el){
2341             return false;
2342         }
2343         return this.el.select('.roo-card-body-ctr',true).first();    
2344     },
2345     
2346     initEvents: function() 
2347     {
2348         this.bodyEl = this.el.select('.card-body',true).first(); 
2349         this.containerEl = this.getChildContainer();
2350         if(this.dragable){
2351             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2352                     containerScroll: true,
2353                     ddGroup: this.drag_group || 'default_card_drag_group'
2354             });
2355             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2356         }
2357         if (this.dropable) {
2358             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2359                 containerScroll: true,
2360                 ddGroup: this.drop_group || 'default_card_drag_group'
2361             });
2362             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2363             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2364             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2365             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2366             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2367         }
2368         
2369         if (this.collapsable) {
2370             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2371         }
2372         if (this.rotateable) {
2373             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2374         }
2375         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2376          
2377         this.footerEl = this.el.select('.card-footer',true).first();
2378         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2379         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2380         this.headerEl = this.el.select('.card-header',true).first();
2381         
2382         if (this.rotated) {
2383             this.el.addClass('roo-card-rotated');
2384             this.fireEvent('rotate', this, true);
2385         }
2386         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2387         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2388         
2389     },
2390     getDragData : function(e)
2391     {
2392         var target = this.getEl();
2393         if (target) {
2394             //this.handleSelection(e);
2395             
2396             var dragData = {
2397                 source: this,
2398                 copy: false,
2399                 nodes: this.getEl(),
2400                 records: []
2401             };
2402             
2403             
2404             dragData.ddel = target.dom ;    // the div element
2405             Roo.log(target.getWidth( ));
2406             dragData.ddel.style.width = target.getWidth() + 'px';
2407             
2408             return dragData;
2409         }
2410         return false;
2411     },
2412     /**
2413     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2414     *    whole Element becomes the target, and this causes the drop gesture to append.
2415     *
2416     *    Returns an object:
2417     *     {
2418            
2419            position : 'below' or 'above'
2420            card  : relateive to card OBJECT (or true for no cards listed)
2421            items_n : relative to nth item in list
2422            card_n : relative to  nth card in list
2423     }
2424     *
2425     *    
2426     */
2427     getTargetFromEvent : function(e, dragged_card_el)
2428     {
2429         var target = e.getTarget();
2430         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2431             target = target.parentNode;
2432         }
2433         
2434         var ret = {
2435             position: '',
2436             cards : [],
2437             card_n : -1,
2438             items_n : -1,
2439             card : false 
2440         };
2441         
2442         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2443         // see if target is one of the 'cards'...
2444         
2445         
2446         //Roo.log(this.items.length);
2447         var pos = false;
2448         
2449         var last_card_n = 0;
2450         var cards_len  = 0;
2451         for (var i = 0;i< this.items.length;i++) {
2452             
2453             if (!this.items[i].el.hasClass('card')) {
2454                  continue;
2455             }
2456             pos = this.getDropPoint(e, this.items[i].el.dom);
2457             
2458             cards_len = ret.cards.length;
2459             //Roo.log(this.items[i].el.dom.id);
2460             ret.cards.push(this.items[i]);
2461             last_card_n  = i;
2462             if (ret.card_n < 0 && pos == 'above') {
2463                 ret.position = cards_len > 0 ? 'below' : pos;
2464                 ret.items_n = i > 0 ? i - 1 : 0;
2465                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2466                 ret.card = ret.cards[ret.card_n];
2467             }
2468         }
2469         if (!ret.cards.length) {
2470             ret.card = true;
2471             ret.position = 'below';
2472             ret.items_n;
2473             return ret;
2474         }
2475         // could not find a card.. stick it at the end..
2476         if (ret.card_n < 0) {
2477             ret.card_n = last_card_n;
2478             ret.card = ret.cards[last_card_n];
2479             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2480             ret.position = 'below';
2481         }
2482         
2483         if (this.items[ret.items_n].el == dragged_card_el) {
2484             return false;
2485         }
2486         
2487         if (ret.position == 'below') {
2488             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2489             
2490             if (card_after  && card_after.el == dragged_card_el) {
2491                 return false;
2492             }
2493             return ret;
2494         }
2495         
2496         // its's after ..
2497         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2498         
2499         if (card_before  && card_before.el == dragged_card_el) {
2500             return false;
2501         }
2502         
2503         return ret;
2504     },
2505     
2506     onNodeEnter : function(n, dd, e, data){
2507         return false;
2508     },
2509     onNodeOver : function(n, dd, e, data)
2510     {
2511        
2512         var target_info = this.getTargetFromEvent(e,data.source.el);
2513         if (target_info === false) {
2514             this.dropPlaceHolder('hide');
2515             return false;
2516         }
2517         Roo.log(['getTargetFromEvent', target_info ]);
2518         
2519         
2520         if (this.fireEvent('cardover', this, [ data ]) === false) {
2521             return false;
2522         }
2523         
2524         this.dropPlaceHolder('show', target_info,data);
2525         
2526         return false; 
2527     },
2528     onNodeOut : function(n, dd, e, data){
2529         this.dropPlaceHolder('hide');
2530      
2531     },
2532     onNodeDrop : function(n, dd, e, data)
2533     {
2534         
2535         // call drop - return false if
2536         
2537         // this could actually fail - if the Network drops..
2538         // we will ignore this at present..- client should probably reload
2539         // the whole set of cards if stuff like that fails.
2540         
2541         
2542         var info = this.getTargetFromEvent(e,data.source.el);
2543         if (info === false) {
2544             return false;
2545         }
2546         this.dropPlaceHolder('hide');
2547   
2548           
2549     
2550         this.acceptCard(data.source, info.position, info.card, info.items_n);
2551         return true;
2552          
2553     },
2554     firstChildCard : function()
2555     {
2556         for (var i = 0;i< this.items.length;i++) {
2557             
2558             if (!this.items[i].el.hasClass('card')) {
2559                  continue;
2560             }
2561             return this.items[i];
2562         }
2563         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2564     },
2565     /**
2566      * accept card
2567      *
2568      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2569      */
2570     acceptCard : function(move_card,  position, next_to_card )
2571     {
2572         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2573             return false;
2574         }
2575         
2576         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2577         
2578         move_card.parent().removeCard(move_card);
2579         
2580         
2581         var dom = move_card.el.dom;
2582         dom.style.width = ''; // clear with - which is set by drag.
2583         
2584         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2585             var cardel = next_to_card.el.dom;
2586             
2587             if (position == 'above' ) {
2588                 cardel.parentNode.insertBefore(dom, cardel);
2589             } else if (cardel.nextSibling) {
2590                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2591             } else {
2592                 cardel.parentNode.append(dom);
2593             }
2594         } else {
2595             // card container???
2596             this.containerEl.dom.append(dom);
2597         }
2598         
2599         //FIXME HANDLE card = true 
2600         
2601         // add this to the correct place in items.
2602         
2603         // remove Card from items.
2604         
2605        
2606         if (this.items.length) {
2607             var nitems = [];
2608             //Roo.log([info.items_n, info.position, this.items.length]);
2609             for (var i =0; i < this.items.length; i++) {
2610                 if (i == to_items_n && position == 'above') {
2611                     nitems.push(move_card);
2612                 }
2613                 nitems.push(this.items[i]);
2614                 if (i == to_items_n && position == 'below') {
2615                     nitems.push(move_card);
2616                 }
2617             }
2618             this.items = nitems;
2619             Roo.log(this.items);
2620         } else {
2621             this.items.push(move_card);
2622         }
2623         
2624         move_card.parentId = this.id;
2625         
2626         return true;
2627         
2628         
2629     },
2630     removeCard : function(c)
2631     {
2632         this.items = this.items.filter(function(e) { return e != c });
2633  
2634         var dom = c.el.dom;
2635         dom.parentNode.removeChild(dom);
2636         dom.style.width = ''; // clear with - which is set by drag.
2637         c.parentId = false;
2638         
2639     },
2640     
2641     /**    Decide whether to drop above or below a View node. */
2642     getDropPoint : function(e, n, dd)
2643     {
2644         if (dd) {
2645              return false;
2646         }
2647         if (n == this.containerEl.dom) {
2648             return "above";
2649         }
2650         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2651         var c = t + (b - t) / 2;
2652         var y = Roo.lib.Event.getPageY(e);
2653         if(y <= c) {
2654             return "above";
2655         }else{
2656             return "below";
2657         }
2658     },
2659     onToggleCollapse : function(e)
2660         {
2661         if (this.collapsed) {
2662             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2663             this.collapsableEl.addClass('show');
2664             this.collapsed = false;
2665             return;
2666         }
2667         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2668         this.collapsableEl.removeClass('show');
2669         this.collapsed = true;
2670         
2671     
2672     },
2673     
2674     onToggleRotate : function(e)
2675     {
2676         this.collapsableEl.removeClass('show');
2677         this.footerEl.removeClass('d-none');
2678         this.el.removeClass('roo-card-rotated');
2679         this.el.removeClass('d-none');
2680         if (this.rotated) {
2681             
2682             this.collapsableEl.addClass('show');
2683             this.rotated = false;
2684             this.fireEvent('rotate', this, this.rotated);
2685             return;
2686         }
2687         this.el.addClass('roo-card-rotated');
2688         this.footerEl.addClass('d-none');
2689         this.el.select('.roo-collapsable').removeClass('show');
2690         
2691         this.rotated = true;
2692         this.fireEvent('rotate', this, this.rotated);
2693     
2694     },
2695     
2696     dropPlaceHolder: function (action, info, data)
2697     {
2698         if (this.dropEl === false) {
2699             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2700             cls : 'd-none'
2701             },true);
2702         }
2703         this.dropEl.removeClass(['d-none', 'd-block']);        
2704         if (action == 'hide') {
2705             
2706             this.dropEl.addClass('d-none');
2707             return;
2708         }
2709         // FIXME - info.card == true!!!
2710         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2711         
2712         if (info.card !== true) {
2713             var cardel = info.card.el.dom;
2714             
2715             if (info.position == 'above') {
2716                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2717             } else if (cardel.nextSibling) {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2719             } else {
2720                 cardel.parentNode.append(this.dropEl.dom);
2721             }
2722         } else {
2723             // card container???
2724             this.containerEl.dom.append(this.dropEl.dom);
2725         }
2726         
2727         this.dropEl.addClass('d-block roo-card-dropzone');
2728         
2729         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2730         
2731         
2732     
2733     
2734     
2735     },
2736     setHeaderText: function(html)
2737     {
2738         this.header = html;
2739         if (this.headerContainerEl) {
2740             this.headerContainerEl.dom.innerHTML = html;
2741         }
2742     },
2743     onHeaderImageLoad : function(ev, he)
2744     {
2745         if (!this.header_image_fit_square) {
2746             return;
2747         }
2748         
2749         var hw = he.naturalHeight / he.naturalWidth;
2750         // wide image = < 0
2751         // tall image = > 1
2752         //var w = he.dom.naturalWidth;
2753         var ww = he.width;
2754         he.style.left =  0;
2755         he.style.position =  'relative';
2756         if (hw > 1) {
2757             var nw = (ww * (1/hw));
2758             Roo.get(he).setSize( ww * (1/hw),  ww);
2759             he.style.left =  ((ww - nw)/ 2) + 'px';
2760             he.style.position =  'relative';
2761         }
2762
2763     }
2764
2765     
2766 });
2767
2768 /*
2769  * - LGPL
2770  *
2771  * Card header - holder for the card header elements.
2772  * 
2773  */
2774
2775 /**
2776  * @class Roo.bootstrap.CardHeader
2777  * @extends Roo.bootstrap.Element
2778  * Bootstrap CardHeader class
2779  * @constructor
2780  * Create a new Card Header - that you can embed children into
2781  * @param {Object} config The config object
2782  */
2783
2784 Roo.bootstrap.CardHeader = function(config){
2785     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2786 };
2787
2788 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2789     
2790     
2791     container_method : 'getCardHeader' 
2792     
2793      
2794     
2795     
2796    
2797 });
2798
2799  
2800
2801  /*
2802  * - LGPL
2803  *
2804  * Card footer - holder for the card footer elements.
2805  * 
2806  */
2807
2808 /**
2809  * @class Roo.bootstrap.CardFooter
2810  * @extends Roo.bootstrap.Element
2811  * Bootstrap CardFooter class
2812  * @constructor
2813  * Create a new Card Footer - that you can embed children into
2814  * @param {Object} config The config object
2815  */
2816
2817 Roo.bootstrap.CardFooter = function(config){
2818     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2819 };
2820
2821 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2822     
2823     
2824     container_method : 'getCardFooter' 
2825     
2826      
2827     
2828     
2829    
2830 });
2831
2832  
2833
2834  /*
2835  * - LGPL
2836  *
2837  * Card header - holder for the card header elements.
2838  * 
2839  */
2840
2841 /**
2842  * @class Roo.bootstrap.CardImageTop
2843  * @extends Roo.bootstrap.Element
2844  * Bootstrap CardImageTop class
2845  * @constructor
2846  * Create a new Card Image Top container
2847  * @param {Object} config The config object
2848  */
2849
2850 Roo.bootstrap.CardImageTop = function(config){
2851     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2852 };
2853
2854 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2855     
2856    
2857     container_method : 'getCardImageTop' 
2858     
2859      
2860     
2861    
2862 });
2863
2864  
2865
2866  
2867 /*
2868 * Licence: LGPL
2869 */
2870
2871 /**
2872  * @class Roo.bootstrap.ButtonUploader
2873  * @extends Roo.bootstrap.Button
2874  * Bootstrap Button Uploader class - it's a button which when you add files to it
2875  *
2876  * 
2877  * @cfg {Number} errorTimeout default 3000
2878  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2879  * @cfg {Array}  html The button text.
2880  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2881  *
2882  * @constructor
2883  * Create a new CardUploader
2884  * @param {Object} config The config object
2885  */
2886
2887 Roo.bootstrap.ButtonUploader = function(config){
2888     
2889  
2890     
2891     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2892     
2893      
2894      this.addEvents({
2895          // raw events
2896         /**
2897          * @event beforeselect
2898          * When button is pressed, before show upload files dialog is shown
2899          * @param {Roo.bootstrap.UploaderButton} this
2900          *
2901          */
2902         'beforeselect' : true,
2903          /**
2904          * @event fired when files have been selected, 
2905          * When a the download link is clicked
2906          * @param {Roo.bootstrap.UploaderButton} this
2907          * @param {Array} Array of files that have been uploaded
2908          */
2909         'uploaded' : true
2910         
2911     });
2912 };
2913  
2914 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2915     
2916      
2917     errorTimeout : 3000,
2918      
2919     images : false,
2920    
2921     fileCollection : false,
2922     allowBlank : true,
2923     
2924     multiple : true,
2925     
2926     getAutoCreate : function()
2927     {
2928         var im = {
2929             tag: 'input',
2930             type : 'file',
2931             cls : 'd-none  roo-card-upload-selector' 
2932           
2933         };
2934         if (this.multiple) {
2935             im.multiple = 'multiple';
2936         }
2937         
2938         return  {
2939             cls :'div' ,
2940             cn : [
2941                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2942                 im
2943
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964          
2965          
2966          
2967         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2968         
2969         this.selectorEl.on('change', this.onFileSelected, this);
2970          
2971          
2972        
2973     },
2974     
2975    
2976     onClick : function(e)
2977     {
2978         e.preventDefault();
2979         
2980         if ( this.fireEvent('beforeselect', this) === false) {
2981             return;
2982         }
2983          
2984         this.selectorEl.dom.click();
2985          
2986     },
2987     
2988     onFileSelected : function(e)
2989     {
2990         e.preventDefault();
2991         
2992         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2993             return;
2994         }
2995         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2996         this.selectorEl.dom.value  = '';// hopefully reset..
2997         
2998         this.fireEvent('uploaded', this,  files );
2999         
3000     },
3001     
3002        
3003    
3004     
3005     /**
3006      * addCard - add an Attachment to the uploader
3007      * @param data - the data about the image to upload
3008      *
3009      * {
3010           id : 123
3011           title : "Title of file",
3012           is_uploaded : false,
3013           src : "http://.....",
3014           srcfile : { the File upload object },
3015           mimetype : file.type,
3016           preview : false,
3017           is_deleted : 0
3018           .. any other data...
3019         }
3020      *
3021      * 
3022     */
3023      
3024     reset: function()
3025     {
3026          
3027          this.selectorEl
3028     } 
3029     
3030     
3031     
3032     
3033 });
3034  /*
3035  * - LGPL
3036  *
3037  * image
3038  * 
3039  */
3040
3041
3042 /**
3043  * @class Roo.bootstrap.Img
3044  * @extends Roo.bootstrap.Component
3045  * Bootstrap Img class
3046  * @cfg {Boolean} imgResponsive false | true
3047  * @cfg {String} border rounded | circle | thumbnail
3048  * @cfg {String} src image source
3049  * @cfg {String} alt image alternative text
3050  * @cfg {String} href a tag href
3051  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3052  * @cfg {String} xsUrl xs image source
3053  * @cfg {String} smUrl sm image source
3054  * @cfg {String} mdUrl md image source
3055  * @cfg {String} lgUrl lg image source
3056  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3057  * 
3058  * @constructor
3059  * Create a new Input
3060  * @param {Object} config The config object
3061  */
3062
3063 Roo.bootstrap.Img = function(config){
3064     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3065     
3066     this.addEvents({
3067         // img events
3068         /**
3069          * @event click
3070          * The img click event for the img.
3071          * @param {Roo.EventObject} e
3072          */
3073         "click" : true,
3074         /**
3075          * @event load
3076          * The when any image loads
3077          * @param {Roo.EventObject} e
3078          */
3079         "load" : true
3080     });
3081 };
3082
3083 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3084     
3085     imgResponsive: true,
3086     border: '',
3087     src: 'about:blank',
3088     href: false,
3089     target: false,
3090     xsUrl: '',
3091     smUrl: '',
3092     mdUrl: '',
3093     lgUrl: '',
3094     backgroundContain : false,
3095
3096     getAutoCreate : function()
3097     {   
3098         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3099             return this.createSingleImg();
3100         }
3101         
3102         var cfg = {
3103             tag: 'div',
3104             cls: 'roo-image-responsive-group',
3105             cn: []
3106         };
3107         var _this = this;
3108         
3109         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3110             
3111             if(!_this[size + 'Url']){
3112                 return;
3113             }
3114             
3115             var img = {
3116                 tag: 'img',
3117                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3118                 html: _this.html || cfg.html,
3119                 src: _this[size + 'Url']
3120             };
3121             
3122             img.cls += ' roo-image-responsive-' + size;
3123             
3124             var s = ['xs', 'sm', 'md', 'lg'];
3125             
3126             s.splice(s.indexOf(size), 1);
3127             
3128             Roo.each(s, function(ss){
3129                 img.cls += ' hidden-' + ss;
3130             });
3131             
3132             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3133                 cfg.cls += ' img-' + _this.border;
3134             }
3135             
3136             if(_this.alt){
3137                 cfg.alt = _this.alt;
3138             }
3139             
3140             if(_this.href){
3141                 var a = {
3142                     tag: 'a',
3143                     href: _this.href,
3144                     cn: [
3145                         img
3146                     ]
3147                 };
3148
3149                 if(this.target){
3150                     a.target = _this.target;
3151                 }
3152             }
3153             
3154             cfg.cn.push((_this.href) ? a : img);
3155             
3156         });
3157         
3158         return cfg;
3159     },
3160     
3161     createSingleImg : function()
3162     {
3163         var cfg = {
3164             tag: 'img',
3165             cls: (this.imgResponsive) ? 'img-responsive' : '',
3166             html : null,
3167             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3168         };
3169         
3170         if (this.backgroundContain) {
3171             cfg.cls += ' background-contain';
3172         }
3173         
3174         cfg.html = this.html || cfg.html;
3175         
3176         if (this.backgroundContain) {
3177             cfg.style="background-image: url(" + this.src + ')';
3178         } else {
3179             cfg.src = this.src || cfg.src;
3180         }
3181         
3182         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3183             cfg.cls += ' img-' + this.border;
3184         }
3185         
3186         if(this.alt){
3187             cfg.alt = this.alt;
3188         }
3189         
3190         if(this.href){
3191             var a = {
3192                 tag: 'a',
3193                 href: this.href,
3194                 cn: [
3195                     cfg
3196                 ]
3197             };
3198             
3199             if(this.target){
3200                 a.target = this.target;
3201             }
3202             
3203         }
3204         
3205         return (this.href) ? a : cfg;
3206     },
3207     
3208     initEvents: function() 
3209     {
3210         if(!this.href){
3211             this.el.on('click', this.onClick, this);
3212         }
3213         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3214             this.el.on('load', this.onImageLoad, this);
3215         } else {
3216             // not sure if this works.. not tested
3217             this.el.select('img', true).on('load', this.onImageLoad, this);
3218         }
3219         
3220     },
3221     
3222     onClick : function(e)
3223     {
3224         Roo.log('img onclick');
3225         this.fireEvent('click', this, e);
3226     },
3227     onImageLoad: function(e)
3228     {
3229         Roo.log('img load');
3230         this.fireEvent('load', this, e);
3231     },
3232     
3233     /**
3234      * Sets the url of the image - used to update it
3235      * @param {String} url the url of the image
3236      */
3237     
3238     setSrc : function(url)
3239     {
3240         this.src =  url;
3241         
3242         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3243             if (this.backgroundContain) {
3244                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3245             } else {
3246                 this.el.dom.src =  url;
3247             }
3248             return;
3249         }
3250         
3251         this.el.select('img', true).first().dom.src =  url;
3252     }
3253     
3254     
3255    
3256 });
3257
3258  /*
3259  * - LGPL
3260  *
3261  * image
3262  * 
3263  */
3264
3265
3266 /**
3267  * @class Roo.bootstrap.Link
3268  * @extends Roo.bootstrap.Component
3269  * Bootstrap Link Class
3270  * @cfg {String} alt image alternative text
3271  * @cfg {String} href a tag href
3272  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3273  * @cfg {String} html the content of the link.
3274  * @cfg {String} anchor name for the anchor link
3275  * @cfg {String} fa - favicon
3276
3277  * @cfg {Boolean} preventDefault (true | false) default false
3278
3279  * 
3280  * @constructor
3281  * Create a new Input
3282  * @param {Object} config The config object
3283  */
3284
3285 Roo.bootstrap.Link = function(config){
3286     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3287     
3288     this.addEvents({
3289         // img events
3290         /**
3291          * @event click
3292          * The img click event for the img.
3293          * @param {Roo.EventObject} e
3294          */
3295         "click" : true
3296     });
3297 };
3298
3299 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3300     
3301     href: false,
3302     target: false,
3303     preventDefault: false,
3304     anchor : false,
3305     alt : false,
3306     fa: false,
3307
3308
3309     getAutoCreate : function()
3310     {
3311         var html = this.html || '';
3312         
3313         if (this.fa !== false) {
3314             html = '<i class="fa fa-' + this.fa + '"></i>';
3315         }
3316         var cfg = {
3317             tag: 'a'
3318         };
3319         // anchor's do not require html/href...
3320         if (this.anchor === false) {
3321             cfg.html = html;
3322             cfg.href = this.href || '#';
3323         } else {
3324             cfg.name = this.anchor;
3325             if (this.html !== false || this.fa !== false) {
3326                 cfg.html = html;
3327             }
3328             if (this.href !== false) {
3329                 cfg.href = this.href;
3330             }
3331         }
3332         
3333         if(this.alt !== false){
3334             cfg.alt = this.alt;
3335         }
3336         
3337         
3338         if(this.target !== false) {
3339             cfg.target = this.target;
3340         }
3341         
3342         return cfg;
3343     },
3344     
3345     initEvents: function() {
3346         
3347         if(!this.href || this.preventDefault){
3348             this.el.on('click', this.onClick, this);
3349         }
3350     },
3351     
3352     onClick : function(e)
3353     {
3354         if(this.preventDefault){
3355             e.preventDefault();
3356         }
3357         //Roo.log('img onclick');
3358         this.fireEvent('click', this, e);
3359     }
3360    
3361 });
3362
3363  /*
3364  * - LGPL
3365  *
3366  * header
3367  * 
3368  */
3369
3370 /**
3371  * @class Roo.bootstrap.Header
3372  * @extends Roo.bootstrap.Component
3373  * Bootstrap Header class
3374  * @cfg {String} html content of header
3375  * @cfg {Number} level (1|2|3|4|5|6) default 1
3376  * 
3377  * @constructor
3378  * Create a new Header
3379  * @param {Object} config The config object
3380  */
3381
3382
3383 Roo.bootstrap.Header  = function(config){
3384     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3385 };
3386
3387 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3388     
3389     //href : false,
3390     html : false,
3391     level : 1,
3392     
3393     
3394     
3395     getAutoCreate : function(){
3396         
3397         
3398         
3399         var cfg = {
3400             tag: 'h' + (1 *this.level),
3401             html: this.html || ''
3402         } ;
3403         
3404         return cfg;
3405     }
3406    
3407 });
3408
3409  
3410
3411  /*
3412  * Based on:
3413  * Ext JS Library 1.1.1
3414  * Copyright(c) 2006-2007, Ext JS, LLC.
3415  *
3416  * Originally Released Under LGPL - original licence link has changed is not relivant.
3417  *
3418  * Fork - LGPL
3419  * <script type="text/javascript">
3420  */
3421  
3422 /**
3423  * @class Roo.bootstrap.MenuMgr
3424  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3425  * @singleton
3426  */
3427 Roo.bootstrap.MenuMgr = function(){
3428    var menus, active, groups = {}, attached = false, lastShow = new Date();
3429
3430    // private - called when first menu is created
3431    function init(){
3432        menus = {};
3433        active = new Roo.util.MixedCollection();
3434        Roo.get(document).addKeyListener(27, function(){
3435            if(active.length > 0){
3436                hideAll();
3437            }
3438        });
3439    }
3440
3441    // private
3442    function hideAll(){
3443        if(active && active.length > 0){
3444            var c = active.clone();
3445            c.each(function(m){
3446                m.hide();
3447            });
3448        }
3449    }
3450
3451    // private
3452    function onHide(m){
3453        active.remove(m);
3454        if(active.length < 1){
3455            Roo.get(document).un("mouseup", onMouseDown);
3456             
3457            attached = false;
3458        }
3459    }
3460
3461    // private
3462    function onShow(m){
3463        var last = active.last();
3464        lastShow = new Date();
3465        active.add(m);
3466        if(!attached){
3467           Roo.get(document).on("mouseup", onMouseDown);
3468            
3469            attached = true;
3470        }
3471        if(m.parentMenu){
3472           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3473           m.parentMenu.activeChild = m;
3474        }else if(last && last.isVisible()){
3475           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3476        }
3477    }
3478
3479    // private
3480    function onBeforeHide(m){
3481        if(m.activeChild){
3482            m.activeChild.hide();
3483        }
3484        if(m.autoHideTimer){
3485            clearTimeout(m.autoHideTimer);
3486            delete m.autoHideTimer;
3487        }
3488    }
3489
3490    // private
3491    function onBeforeShow(m){
3492        var pm = m.parentMenu;
3493        if(!pm && !m.allowOtherMenus){
3494            hideAll();
3495        }else if(pm && pm.activeChild && active != m){
3496            pm.activeChild.hide();
3497        }
3498    }
3499
3500    // private this should really trigger on mouseup..
3501    function onMouseDown(e){
3502         Roo.log("on Mouse Up");
3503         
3504         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3505             Roo.log("MenuManager hideAll");
3506             hideAll();
3507             e.stopEvent();
3508         }
3509         
3510         
3511    }
3512
3513    // private
3514    function onBeforeCheck(mi, state){
3515        if(state){
3516            var g = groups[mi.group];
3517            for(var i = 0, l = g.length; i < l; i++){
3518                if(g[i] != mi){
3519                    g[i].setChecked(false);
3520                }
3521            }
3522        }
3523    }
3524
3525    return {
3526
3527        /**
3528         * Hides all menus that are currently visible
3529         */
3530        hideAll : function(){
3531             hideAll();  
3532        },
3533
3534        // private
3535        register : function(menu){
3536            if(!menus){
3537                init();
3538            }
3539            menus[menu.id] = menu;
3540            menu.on("beforehide", onBeforeHide);
3541            menu.on("hide", onHide);
3542            menu.on("beforeshow", onBeforeShow);
3543            menu.on("show", onShow);
3544            var g = menu.group;
3545            if(g && menu.events["checkchange"]){
3546                if(!groups[g]){
3547                    groups[g] = [];
3548                }
3549                groups[g].push(menu);
3550                menu.on("checkchange", onCheck);
3551            }
3552        },
3553
3554         /**
3555          * Returns a {@link Roo.menu.Menu} object
3556          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3557          * be used to generate and return a new Menu instance.
3558          */
3559        get : function(menu){
3560            if(typeof menu == "string"){ // menu id
3561                return menus[menu];
3562            }else if(menu.events){  // menu instance
3563                return menu;
3564            }
3565            /*else if(typeof menu.length == 'number'){ // array of menu items?
3566                return new Roo.bootstrap.Menu({items:menu});
3567            }else{ // otherwise, must be a config
3568                return new Roo.bootstrap.Menu(menu);
3569            }
3570            */
3571            return false;
3572        },
3573
3574        // private
3575        unregister : function(menu){
3576            delete menus[menu.id];
3577            menu.un("beforehide", onBeforeHide);
3578            menu.un("hide", onHide);
3579            menu.un("beforeshow", onBeforeShow);
3580            menu.un("show", onShow);
3581            var g = menu.group;
3582            if(g && menu.events["checkchange"]){
3583                groups[g].remove(menu);
3584                menu.un("checkchange", onCheck);
3585            }
3586        },
3587
3588        // private
3589        registerCheckable : function(menuItem){
3590            var g = menuItem.group;
3591            if(g){
3592                if(!groups[g]){
3593                    groups[g] = [];
3594                }
3595                groups[g].push(menuItem);
3596                menuItem.on("beforecheckchange", onBeforeCheck);
3597            }
3598        },
3599
3600        // private
3601        unregisterCheckable : function(menuItem){
3602            var g = menuItem.group;
3603            if(g){
3604                groups[g].remove(menuItem);
3605                menuItem.un("beforecheckchange", onBeforeCheck);
3606            }
3607        }
3608    };
3609 }();/*
3610  * - LGPL
3611  *
3612  * menu
3613  * 
3614  */
3615
3616 /**
3617  * @class Roo.bootstrap.Menu
3618  * @extends Roo.bootstrap.Component
3619  * Bootstrap Menu class - container for MenuItems
3620  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3621  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3622  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3623  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3624   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3625   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3626  
3627  * @constructor
3628  * Create a new Menu
3629  * @param {Object} config The config object
3630  */
3631
3632
3633 Roo.bootstrap.Menu = function(config){
3634     
3635     if (config.type == 'treeview') {
3636         // normally menu's are drawn attached to the document to handle layering etc..
3637         // however treeview (used by the docs menu is drawn into the parent element)
3638         this.container_method = 'getChildContainer'; 
3639     }
3640     
3641     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3642     if (this.registerMenu && this.type != 'treeview')  {
3643         Roo.bootstrap.MenuMgr.register(this);
3644     }
3645     
3646     
3647     this.addEvents({
3648         /**
3649          * @event beforeshow
3650          * Fires before this menu is displayed (return false to block)
3651          * @param {Roo.menu.Menu} this
3652          */
3653         beforeshow : true,
3654         /**
3655          * @event beforehide
3656          * Fires before this menu is hidden (return false to block)
3657          * @param {Roo.menu.Menu} this
3658          */
3659         beforehide : true,
3660         /**
3661          * @event show
3662          * Fires after this menu is displayed
3663          * @param {Roo.menu.Menu} this
3664          */
3665         show : true,
3666         /**
3667          * @event hide
3668          * Fires after this menu is hidden
3669          * @param {Roo.menu.Menu} this
3670          */
3671         hide : true,
3672         /**
3673          * @event click
3674          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3675          * @param {Roo.menu.Menu} this
3676          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677          * @param {Roo.EventObject} e
3678          */
3679         click : true,
3680         /**
3681          * @event mouseover
3682          * Fires when the mouse is hovering over this menu
3683          * @param {Roo.menu.Menu} this
3684          * @param {Roo.EventObject} e
3685          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3686          */
3687         mouseover : true,
3688         /**
3689          * @event mouseout
3690          * Fires when the mouse exits this menu
3691          * @param {Roo.menu.Menu} this
3692          * @param {Roo.EventObject} e
3693          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3694          */
3695         mouseout : true,
3696         /**
3697          * @event itemclick
3698          * Fires when a menu item contained in this menu is clicked
3699          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3700          * @param {Roo.EventObject} e
3701          */
3702         itemclick: true
3703     });
3704     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3705 };
3706
3707 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3708     
3709    /// html : false,
3710    
3711     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3712     type: false,
3713     /**
3714      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3715      */
3716     registerMenu : true,
3717     
3718     menuItems :false, // stores the menu items..
3719     
3720     hidden:true,
3721         
3722     parentMenu : false,
3723     
3724     stopEvent : true,
3725     
3726     isLink : false,
3727     
3728     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3729     
3730     hideTrigger : false,
3731     
3732     align : 'tl-bl?',
3733     
3734     
3735     getChildContainer : function() {
3736         return this.el;  
3737     },
3738     
3739     getAutoCreate : function(){
3740          
3741         //if (['right'].indexOf(this.align)!==-1) {
3742         //    cfg.cn[1].cls += ' pull-right'
3743         //}
3744          
3745         var cfg = {
3746             tag : 'ul',
3747             cls : 'dropdown-menu shadow' ,
3748             style : 'z-index:1000'
3749             
3750         };
3751         
3752         if (this.type === 'submenu') {
3753             cfg.cls = 'submenu active';
3754         }
3755         if (this.type === 'treeview') {
3756             cfg.cls = 'treeview-menu';
3757         }
3758         
3759         return cfg;
3760     },
3761     initEvents : function() {
3762         
3763        // Roo.log("ADD event");
3764        // Roo.log(this.triggerEl.dom);
3765         if (this.triggerEl) {
3766             
3767             this.triggerEl.on('click', this.onTriggerClick, this);
3768             
3769             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3770             
3771             if (!this.hideTrigger) {
3772                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3773                     // dropdown toggle on the 'a' in BS4?
3774                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3775                 } else {
3776                     this.triggerEl.addClass('dropdown-toggle');
3777                 }
3778             }
3779         }
3780         
3781         if (Roo.isTouch) {
3782             this.el.on('touchstart'  , this.onTouch, this);
3783         }
3784         this.el.on('click' , this.onClick, this);
3785
3786         this.el.on("mouseover", this.onMouseOver, this);
3787         this.el.on("mouseout", this.onMouseOut, this);
3788         
3789     },
3790     
3791     findTargetItem : function(e)
3792     {
3793         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3794         if(!t){
3795             return false;
3796         }
3797         //Roo.log(t);         Roo.log(t.id);
3798         if(t && t.id){
3799             //Roo.log(this.menuitems);
3800             return this.menuitems.get(t.id);
3801             
3802             //return this.items.get(t.menuItemId);
3803         }
3804         
3805         return false;
3806     },
3807     
3808     onTouch : function(e) 
3809     {
3810         Roo.log("menu.onTouch");
3811         //e.stopEvent(); this make the user popdown broken
3812         this.onClick(e);
3813     },
3814     
3815     onClick : function(e)
3816     {
3817         Roo.log("menu.onClick");
3818         
3819         var t = this.findTargetItem(e);
3820         if(!t || t.isContainer){
3821             return;
3822         }
3823         Roo.log(e);
3824         /*
3825         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3826             if(t == this.activeItem && t.shouldDeactivate(e)){
3827                 this.activeItem.deactivate();
3828                 delete this.activeItem;
3829                 return;
3830             }
3831             if(t.canActivate){
3832                 this.setActiveItem(t, true);
3833             }
3834             return;
3835             
3836             
3837         }
3838         */
3839        
3840         Roo.log('pass click event');
3841         
3842         t.onClick(e);
3843         
3844         this.fireEvent("click", this, t, e);
3845         
3846         var _this = this;
3847         
3848         if(!t.href.length || t.href == '#'){
3849             (function() { _this.hide(); }).defer(100);
3850         }
3851         
3852     },
3853     
3854     onMouseOver : function(e){
3855         var t  = this.findTargetItem(e);
3856         //Roo.log(t);
3857         //if(t){
3858         //    if(t.canActivate && !t.disabled){
3859         //        this.setActiveItem(t, true);
3860         //    }
3861         //}
3862         
3863         this.fireEvent("mouseover", this, e, t);
3864     },
3865     isVisible : function(){
3866         return !this.hidden;
3867     },
3868     onMouseOut : function(e){
3869         var t  = this.findTargetItem(e);
3870         
3871         //if(t ){
3872         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3873         //        this.activeItem.deactivate();
3874         //        delete this.activeItem;
3875         //    }
3876         //}
3877         this.fireEvent("mouseout", this, e, t);
3878     },
3879     
3880     
3881     /**
3882      * Displays this menu relative to another element
3883      * @param {String/HTMLElement/Roo.Element} element The element to align to
3884      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3885      * the element (defaults to this.defaultAlign)
3886      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887      */
3888     show : function(el, pos, parentMenu)
3889     {
3890         if (false === this.fireEvent("beforeshow", this)) {
3891             Roo.log("show canceled");
3892             return;
3893         }
3894         this.parentMenu = parentMenu;
3895         if(!this.el){
3896             this.render();
3897         }
3898         this.el.addClass('show'); // show otherwise we do not know how big we are..
3899          
3900         var xy = this.el.getAlignToXY(el, pos);
3901         
3902         // bl-tl << left align  below
3903         // tl-bl << left align 
3904         
3905         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3906             // if it goes to far to the right.. -> align left.
3907             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3908         }
3909         if(xy[0] < 0){
3910             // was left align - go right?
3911             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3912         }
3913         
3914         // goes down the bottom
3915         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3916            xy[1]  < 0 ){
3917             var a = this.align.replace('?', '').split('-');
3918             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3919             
3920         }
3921         
3922         this.showAt(  xy , parentMenu, false);
3923     },
3924      /**
3925      * Displays this menu at a specific xy position
3926      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3927      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3928      */
3929     showAt : function(xy, parentMenu, /* private: */_e){
3930         this.parentMenu = parentMenu;
3931         if(!this.el){
3932             this.render();
3933         }
3934         if(_e !== false){
3935             this.fireEvent("beforeshow", this);
3936             //xy = this.el.adjustForConstraints(xy);
3937         }
3938         
3939         //this.el.show();
3940         this.hideMenuItems();
3941         this.hidden = false;
3942         if (this.triggerEl) {
3943             this.triggerEl.addClass('open');
3944         }
3945         
3946         this.el.addClass('show');
3947         
3948         
3949         
3950         // reassign x when hitting right
3951         
3952         // reassign y when hitting bottom
3953         
3954         // but the list may align on trigger left or trigger top... should it be a properity?
3955         
3956         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3957             this.el.setXY(xy);
3958         }
3959         
3960         this.focus();
3961         this.fireEvent("show", this);
3962     },
3963     
3964     focus : function(){
3965         return;
3966         if(!this.hidden){
3967             this.doFocus.defer(50, this);
3968         }
3969     },
3970
3971     doFocus : function(){
3972         if(!this.hidden){
3973             this.focusEl.focus();
3974         }
3975     },
3976
3977     /**
3978      * Hides this menu and optionally all parent menus
3979      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3980      */
3981     hide : function(deep)
3982     {
3983         if (false === this.fireEvent("beforehide", this)) {
3984             Roo.log("hide canceled");
3985             return;
3986         }
3987         this.hideMenuItems();
3988         if(this.el && this.isVisible()){
3989            
3990             if(this.activeItem){
3991                 this.activeItem.deactivate();
3992                 this.activeItem = null;
3993             }
3994             if (this.triggerEl) {
3995                 this.triggerEl.removeClass('open');
3996             }
3997             
3998             this.el.removeClass('show');
3999             this.hidden = true;
4000             this.fireEvent("hide", this);
4001         }
4002         if(deep === true && this.parentMenu){
4003             this.parentMenu.hide(true);
4004         }
4005     },
4006     
4007     onTriggerClick : function(e)
4008     {
4009         Roo.log('trigger click');
4010         
4011         var target = e.getTarget();
4012         
4013         Roo.log(target.nodeName.toLowerCase());
4014         
4015         if(target.nodeName.toLowerCase() === 'i'){
4016             e.preventDefault();
4017         }
4018         
4019     },
4020     
4021     onTriggerPress  : function(e)
4022     {
4023         Roo.log('trigger press');
4024         //Roo.log(e.getTarget());
4025        // Roo.log(this.triggerEl.dom);
4026        
4027         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4028         var pel = Roo.get(e.getTarget());
4029         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4030             Roo.log('is treeview or dropdown?');
4031             return;
4032         }
4033         
4034         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4035             return;
4036         }
4037         
4038         if (this.isVisible()) {
4039             Roo.log('hide');
4040             this.hide();
4041         } else {
4042             Roo.log('show');
4043             
4044             this.show(this.triggerEl, this.align, false);
4045         }
4046         
4047         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4048             e.stopEvent();
4049         }
4050         
4051     },
4052        
4053     
4054     hideMenuItems : function()
4055     {
4056         Roo.log("hide Menu Items");
4057         if (!this.el) { 
4058             return;
4059         }
4060         
4061         this.el.select('.open',true).each(function(aa) {
4062             
4063             aa.removeClass('open');
4064          
4065         });
4066     },
4067     addxtypeChild : function (tree, cntr) {
4068         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4069           
4070         this.menuitems.add(comp);
4071         return comp;
4072
4073     },
4074     getEl : function()
4075     {
4076         Roo.log(this.el);
4077         return this.el;
4078     },
4079     
4080     clear : function()
4081     {
4082         this.getEl().dom.innerHTML = '';
4083         this.menuitems.clear();
4084     }
4085 });
4086
4087  
4088  /*
4089  * - LGPL
4090  *
4091  * menu item
4092  * 
4093  */
4094
4095
4096 /**
4097  * @class Roo.bootstrap.MenuItem
4098  * @extends Roo.bootstrap.Component
4099  * Bootstrap MenuItem class
4100  * @cfg {String} html the menu label
4101  * @cfg {String} href the link
4102  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4103  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4104  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4105  * @cfg {String} fa favicon to show on left of menu item.
4106  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107  * 
4108  * 
4109  * @constructor
4110  * Create a new MenuItem
4111  * @param {Object} config The config object
4112  */
4113
4114
4115 Roo.bootstrap.MenuItem = function(config){
4116     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4117     this.addEvents({
4118         // raw events
4119         /**
4120          * @event click
4121          * The raw click event for the entire grid.
4122          * @param {Roo.bootstrap.MenuItem} this
4123          * @param {Roo.EventObject} e
4124          */
4125         "click" : true
4126     });
4127 };
4128
4129 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4130     
4131     href : false,
4132     html : false,
4133     preventDefault: false,
4134     isContainer : false,
4135     active : false,
4136     fa: false,
4137     
4138     getAutoCreate : function(){
4139         
4140         if(this.isContainer){
4141             return {
4142                 tag: 'li',
4143                 cls: 'dropdown-menu-item '
4144             };
4145         }
4146         var ctag = {
4147             tag: 'span',
4148             html: 'Link'
4149         };
4150         
4151         var anc = {
4152             tag : 'a',
4153             cls : 'dropdown-item',
4154             href : '#',
4155             cn : [  ]
4156         };
4157         
4158         if (this.fa !== false) {
4159             anc.cn.push({
4160                 tag : 'i',
4161                 cls : 'fa fa-' + this.fa
4162             });
4163         }
4164         
4165         anc.cn.push(ctag);
4166         
4167         
4168         var cfg= {
4169             tag: 'li',
4170             cls: 'dropdown-menu-item',
4171             cn: [ anc ]
4172         };
4173         if (this.parent().type == 'treeview') {
4174             cfg.cls = 'treeview-menu';
4175         }
4176         if (this.active) {
4177             cfg.cls += ' active';
4178         }
4179         
4180         
4181         
4182         anc.href = this.href || cfg.cn[0].href ;
4183         ctag.html = this.html || cfg.cn[0].html ;
4184         return cfg;
4185     },
4186     
4187     initEvents: function()
4188     {
4189         if (this.parent().type == 'treeview') {
4190             this.el.select('a').on('click', this.onClick, this);
4191         }
4192         
4193         if (this.menu) {
4194             this.menu.parentType = this.xtype;
4195             this.menu.triggerEl = this.el;
4196             this.menu = this.addxtype(Roo.apply({}, this.menu));
4197         }
4198         
4199     },
4200     onClick : function(e)
4201     {
4202         Roo.log('item on click ');
4203         
4204         if(this.preventDefault){
4205             e.preventDefault();
4206         }
4207         //this.parent().hideMenuItems();
4208         
4209         this.fireEvent('click', this, e);
4210     },
4211     getEl : function()
4212     {
4213         return this.el;
4214     } 
4215 });
4216
4217  
4218
4219  /*
4220  * - LGPL
4221  *
4222  * menu separator
4223  * 
4224  */
4225
4226
4227 /**
4228  * @class Roo.bootstrap.MenuSeparator
4229  * @extends Roo.bootstrap.Component
4230  * Bootstrap MenuSeparator class
4231  * 
4232  * @constructor
4233  * Create a new MenuItem
4234  * @param {Object} config The config object
4235  */
4236
4237
4238 Roo.bootstrap.MenuSeparator = function(config){
4239     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4240 };
4241
4242 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4243     
4244     getAutoCreate : function(){
4245         var cfg = {
4246             cls: 'divider',
4247             tag : 'li'
4248         };
4249         
4250         return cfg;
4251     }
4252    
4253 });
4254
4255  
4256
4257  
4258 /*
4259 * Licence: LGPL
4260 */
4261
4262 /**
4263  * @class Roo.bootstrap.Modal
4264  * @extends Roo.bootstrap.Component
4265  * @builder-top
4266  * @parent none
4267  * @children Roo.bootstrap.Component
4268  * Bootstrap Modal class
4269  * @cfg {String} title Title of dialog
4270  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4271  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4272  * @cfg {Boolean} specificTitle default false
4273  * @cfg {Array} buttons Array of buttons or standard button set..
4274  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4275  * @cfg {Boolean} animate default true
4276  * @cfg {Boolean} allow_close default true
4277  * @cfg {Boolean} fitwindow default false
4278  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4279  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4280  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4281  * @cfg {String} size (sm|lg|xl) default empty
4282  * @cfg {Number} max_width set the max width of modal
4283  * @cfg {Boolean} editableTitle can the title be edited
4284
4285  *
4286  *
4287  * @constructor
4288  * Create a new Modal Dialog
4289  * @param {Object} config The config object
4290  */
4291
4292 Roo.bootstrap.Modal = function(config){
4293     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4294     this.addEvents({
4295         // raw events
4296         /**
4297          * @event btnclick
4298          * The raw btnclick event for the button
4299          * @param {Roo.EventObject} e
4300          */
4301         "btnclick" : true,
4302         /**
4303          * @event resize
4304          * Fire when dialog resize
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} e
4307          */
4308         "resize" : true,
4309         /**
4310          * @event titlechanged
4311          * Fire when the editable title has been changed
4312          * @param {Roo.bootstrap.Modal} this
4313          * @param {Roo.EventObject} value
4314          */
4315         "titlechanged" : true 
4316         
4317     });
4318     this.buttons = this.buttons || [];
4319
4320     if (this.tmpl) {
4321         this.tmpl = Roo.factory(this.tmpl);
4322     }
4323
4324 };
4325
4326 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4327
4328     title : 'test dialog',
4329
4330     buttons : false,
4331
4332     // set on load...
4333
4334     html: false,
4335
4336     tmp: false,
4337
4338     specificTitle: false,
4339
4340     buttonPosition: 'right',
4341
4342     allow_close : true,
4343
4344     animate : true,
4345
4346     fitwindow: false,
4347     
4348      // private
4349     dialogEl: false,
4350     bodyEl:  false,
4351     footerEl:  false,
4352     titleEl:  false,
4353     closeEl:  false,
4354
4355     size: '',
4356     
4357     max_width: 0,
4358     
4359     max_height: 0,
4360     
4361     fit_content: false,
4362     editableTitle  : false,
4363
4364     onRender : function(ct, position)
4365     {
4366         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4367
4368         if(!this.el){
4369             var cfg = Roo.apply({},  this.getAutoCreate());
4370             cfg.id = Roo.id();
4371             //if(!cfg.name){
4372             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4373             //}
4374             //if (!cfg.name.length) {
4375             //    delete cfg.name;
4376            // }
4377             if (this.cls) {
4378                 cfg.cls += ' ' + this.cls;
4379             }
4380             if (this.style) {
4381                 cfg.style = this.style;
4382             }
4383             this.el = Roo.get(document.body).createChild(cfg, position);
4384         }
4385         //var type = this.el.dom.type;
4386
4387
4388         if(this.tabIndex !== undefined){
4389             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4390         }
4391
4392         this.dialogEl = this.el.select('.modal-dialog',true).first();
4393         this.bodyEl = this.el.select('.modal-body',true).first();
4394         this.closeEl = this.el.select('.modal-header .close', true).first();
4395         this.headerEl = this.el.select('.modal-header',true).first();
4396         this.titleEl = this.el.select('.modal-title',true).first();
4397         this.footerEl = this.el.select('.modal-footer',true).first();
4398
4399         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4400         
4401         //this.el.addClass("x-dlg-modal");
4402
4403         if (this.buttons.length) {
4404             Roo.each(this.buttons, function(bb) {
4405                 var b = Roo.apply({}, bb);
4406                 b.xns = b.xns || Roo.bootstrap;
4407                 b.xtype = b.xtype || 'Button';
4408                 if (typeof(b.listeners) == 'undefined') {
4409                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4410                 }
4411
4412                 var btn = Roo.factory(b);
4413
4414                 btn.render(this.getButtonContainer());
4415
4416             },this);
4417         }
4418         // render the children.
4419         var nitems = [];
4420
4421         if(typeof(this.items) != 'undefined'){
4422             var items = this.items;
4423             delete this.items;
4424
4425             for(var i =0;i < items.length;i++) {
4426                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4427             }
4428         }
4429
4430         this.items = nitems;
4431
4432         // where are these used - they used to be body/close/footer
4433
4434
4435         this.initEvents();
4436         //this.el.addClass([this.fieldClass, this.cls]);
4437
4438     },
4439
4440     getAutoCreate : function()
4441     {
4442         // we will default to modal-body-overflow - might need to remove or make optional later.
4443         var bdy = {
4444                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4445                 html : this.html || ''
4446         };
4447
4448         var title = {
4449             tag: 'h5',
4450             cls : 'modal-title',
4451             html : this.title
4452         };
4453
4454         if(this.specificTitle){ // WTF is this?
4455             title = this.title;
4456         }
4457
4458         var header = [];
4459         if (this.allow_close && Roo.bootstrap.version == 3) {
4460             header.push({
4461                 tag: 'button',
4462                 cls : 'close',
4463                 html : '&times'
4464             });
4465         }
4466
4467         header.push(title);
4468
4469         if (this.editableTitle) {
4470             header.push({
4471                 cls: 'form-control roo-editable-title d-none',
4472                 tag: 'input',
4473                 type: 'text'
4474             });
4475         }
4476         
4477         if (this.allow_close && Roo.bootstrap.version == 4) {
4478             header.push({
4479                 tag: 'button',
4480                 cls : 'close',
4481                 html : '&times'
4482             });
4483         }
4484         
4485         var size = '';
4486
4487         if(this.size.length){
4488             size = 'modal-' + this.size;
4489         }
4490         
4491         var footer = Roo.bootstrap.version == 3 ?
4492             {
4493                 cls : 'modal-footer',
4494                 cn : [
4495                     {
4496                         tag: 'div',
4497                         cls: 'btn-' + this.buttonPosition
4498                     }
4499                 ]
4500
4501             } :
4502             {  // BS4 uses mr-auto on left buttons....
4503                 cls : 'modal-footer'
4504             };
4505
4506             
4507
4508         
4509         
4510         var modal = {
4511             cls: "modal",
4512              cn : [
4513                 {
4514                     cls: "modal-dialog " + size,
4515                     cn : [
4516                         {
4517                             cls : "modal-content",
4518                             cn : [
4519                                 {
4520                                     cls : 'modal-header',
4521                                     cn : header
4522                                 },
4523                                 bdy,
4524                                 footer
4525                             ]
4526
4527                         }
4528                     ]
4529
4530                 }
4531             ]
4532         };
4533
4534         if(this.animate){
4535             modal.cls += ' fade';
4536         }
4537
4538         return modal;
4539
4540     },
4541     getChildContainer : function() {
4542
4543          return this.bodyEl;
4544
4545     },
4546     getButtonContainer : function() {
4547         
4548          return Roo.bootstrap.version == 4 ?
4549             this.el.select('.modal-footer',true).first()
4550             : this.el.select('.modal-footer div',true).first();
4551
4552     },
4553     initEvents : function()
4554     {
4555         if (this.allow_close) {
4556             this.closeEl.on('click', this.hide, this);
4557         }
4558         Roo.EventManager.onWindowResize(this.resize, this, true);
4559         if (this.editableTitle) {
4560             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4561             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4562             this.headerEditEl.on('keyup', function(e) {
4563                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4564                         this.toggleHeaderInput(false)
4565                     }
4566                 }, this);
4567             this.headerEditEl.on('blur', function(e) {
4568                 this.toggleHeaderInput(false)
4569             },this);
4570         }
4571
4572     },
4573   
4574
4575     resize : function()
4576     {
4577         this.maskEl.setSize(
4578             Roo.lib.Dom.getViewWidth(true),
4579             Roo.lib.Dom.getViewHeight(true)
4580         );
4581         
4582         if (this.fitwindow) {
4583             
4584            this.dialogEl.setStyle( { 'max-width' : '100%' });
4585             this.setSize(
4586                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4587                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4588             );
4589             return;
4590         }
4591         
4592         if(this.max_width !== 0) {
4593             
4594             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4595             
4596             if(this.height) {
4597                 this.setSize(w, this.height);
4598                 return;
4599             }
4600             
4601             if(this.max_height) {
4602                 this.setSize(w,Math.min(
4603                     this.max_height,
4604                     Roo.lib.Dom.getViewportHeight(true) - 60
4605                 ));
4606                 
4607                 return;
4608             }
4609             
4610             if(!this.fit_content) {
4611                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4612                 return;
4613             }
4614             
4615             this.setSize(w, Math.min(
4616                 60 +
4617                 this.headerEl.getHeight() + 
4618                 this.footerEl.getHeight() + 
4619                 this.getChildHeight(this.bodyEl.dom.childNodes),
4620                 Roo.lib.Dom.getViewportHeight(true) - 60)
4621             );
4622         }
4623         
4624     },
4625
4626     setSize : function(w,h)
4627     {
4628         if (!w && !h) {
4629             return;
4630         }
4631         
4632         this.resizeTo(w,h);
4633     },
4634
4635     show : function() {
4636
4637         if (!this.rendered) {
4638             this.render();
4639         }
4640         this.toggleHeaderInput(false);
4641         //this.el.setStyle('display', 'block');
4642         this.el.removeClass('hideing');
4643         this.el.dom.style.display='block';
4644         
4645         Roo.get(document.body).addClass('modal-open');
4646  
4647         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4648             
4649             (function(){
4650                 this.el.addClass('show');
4651                 this.el.addClass('in');
4652             }).defer(50, this);
4653         }else{
4654             this.el.addClass('show');
4655             this.el.addClass('in');
4656         }
4657
4658         // not sure how we can show data in here..
4659         //if (this.tmpl) {
4660         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4661         //}
4662
4663         Roo.get(document.body).addClass("x-body-masked");
4664         
4665         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4666         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4667         this.maskEl.dom.style.display = 'block';
4668         this.maskEl.addClass('show');
4669         
4670         
4671         this.resize();
4672         
4673         this.fireEvent('show', this);
4674
4675         // set zindex here - otherwise it appears to be ignored...
4676         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4677
4678         (function () {
4679             this.items.forEach( function(e) {
4680                 e.layout ? e.layout() : false;
4681
4682             });
4683         }).defer(100,this);
4684
4685     },
4686     hide : function()
4687     {
4688         if(this.fireEvent("beforehide", this) !== false){
4689             
4690             this.maskEl.removeClass('show');
4691             
4692             this.maskEl.dom.style.display = '';
4693             Roo.get(document.body).removeClass("x-body-masked");
4694             this.el.removeClass('in');
4695             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4696
4697             if(this.animate){ // why
4698                 this.el.addClass('hideing');
4699                 this.el.removeClass('show');
4700                 (function(){
4701                     if (!this.el.hasClass('hideing')) {
4702                         return; // it's been shown again...
4703                     }
4704                     
4705                     this.el.dom.style.display='';
4706
4707                     Roo.get(document.body).removeClass('modal-open');
4708                     this.el.removeClass('hideing');
4709                 }).defer(150,this);
4710                 
4711             }else{
4712                 this.el.removeClass('show');
4713                 this.el.dom.style.display='';
4714                 Roo.get(document.body).removeClass('modal-open');
4715
4716             }
4717             this.fireEvent('hide', this);
4718         }
4719     },
4720     isVisible : function()
4721     {
4722         
4723         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4724         
4725     },
4726
4727     addButton : function(str, cb)
4728     {
4729
4730
4731         var b = Roo.apply({}, { html : str } );
4732         b.xns = b.xns || Roo.bootstrap;
4733         b.xtype = b.xtype || 'Button';
4734         if (typeof(b.listeners) == 'undefined') {
4735             b.listeners = { click : cb.createDelegate(this)  };
4736         }
4737
4738         var btn = Roo.factory(b);
4739
4740         btn.render(this.getButtonContainer());
4741
4742         return btn;
4743
4744     },
4745
4746     setDefaultButton : function(btn)
4747     {
4748         //this.el.select('.modal-footer').()
4749     },
4750
4751     resizeTo: function(w,h)
4752     {
4753         this.dialogEl.setWidth(w);
4754         
4755         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4756
4757         this.bodyEl.setHeight(h - diff);
4758         
4759         this.fireEvent('resize', this);
4760     },
4761     
4762     setContentSize  : function(w, h)
4763     {
4764
4765     },
4766     onButtonClick: function(btn,e)
4767     {
4768         //Roo.log([a,b,c]);
4769         this.fireEvent('btnclick', btn.name, e);
4770     },
4771      /**
4772      * Set the title of the Dialog
4773      * @param {String} str new Title
4774      */
4775     setTitle: function(str) {
4776         this.titleEl.dom.innerHTML = str;
4777         this.title = str;
4778     },
4779     /**
4780      * Set the body of the Dialog
4781      * @param {String} str new Title
4782      */
4783     setBody: function(str) {
4784         this.bodyEl.dom.innerHTML = str;
4785     },
4786     /**
4787      * Set the body of the Dialog using the template
4788      * @param {Obj} data - apply this data to the template and replace the body contents.
4789      */
4790     applyBody: function(obj)
4791     {
4792         if (!this.tmpl) {
4793             Roo.log("Error - using apply Body without a template");
4794             //code
4795         }
4796         this.tmpl.overwrite(this.bodyEl, obj);
4797     },
4798     
4799     getChildHeight : function(child_nodes)
4800     {
4801         if(
4802             !child_nodes ||
4803             child_nodes.length == 0
4804         ) {
4805             return 0;
4806         }
4807         
4808         var child_height = 0;
4809         
4810         for(var i = 0; i < child_nodes.length; i++) {
4811             
4812             /*
4813             * for modal with tabs...
4814             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4815                 
4816                 var layout_childs = child_nodes[i].childNodes;
4817                 
4818                 for(var j = 0; j < layout_childs.length; j++) {
4819                     
4820                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4821                         
4822                         var layout_body_childs = layout_childs[j].childNodes;
4823                         
4824                         for(var k = 0; k < layout_body_childs.length; k++) {
4825                             
4826                             if(layout_body_childs[k].classList.contains('navbar')) {
4827                                 child_height += layout_body_childs[k].offsetHeight;
4828                                 continue;
4829                             }
4830                             
4831                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4832                                 
4833                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4834                                 
4835                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4836                                     
4837                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4838                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4839                                         continue;
4840                                     }
4841                                     
4842                                 }
4843                                 
4844                             }
4845                             
4846                         }
4847                     }
4848                 }
4849                 continue;
4850             }
4851             */
4852             
4853             child_height += child_nodes[i].offsetHeight;
4854             // Roo.log(child_nodes[i].offsetHeight);
4855         }
4856         
4857         return child_height;
4858     },
4859     toggleHeaderInput : function(is_edit)
4860     {
4861         if (!this.editableTitle) {
4862             return; // not editable.
4863         }
4864         if (is_edit && this.is_header_editing) {
4865             return; // already editing..
4866         }
4867         if (is_edit) {
4868     
4869             this.headerEditEl.dom.value = this.title;
4870             this.headerEditEl.removeClass('d-none');
4871             this.headerEditEl.dom.focus();
4872             this.titleEl.addClass('d-none');
4873             
4874             this.is_header_editing = true;
4875             return
4876         }
4877         // flip back to not editing.
4878         this.title = this.headerEditEl.dom.value;
4879         this.headerEditEl.addClass('d-none');
4880         this.titleEl.removeClass('d-none');
4881         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4882         this.is_header_editing = false;
4883         this.fireEvent('titlechanged', this, this.title);
4884     
4885             
4886         
4887     }
4888
4889 });
4890
4891
4892 Roo.apply(Roo.bootstrap.Modal,  {
4893     /**
4894          * Button config that displays a single OK button
4895          * @type Object
4896          */
4897         OK :  [{
4898             name : 'ok',
4899             weight : 'primary',
4900             html : 'OK'
4901         }],
4902         /**
4903          * Button config that displays Yes and No buttons
4904          * @type Object
4905          */
4906         YESNO : [
4907             {
4908                 name  : 'no',
4909                 html : 'No'
4910             },
4911             {
4912                 name  :'yes',
4913                 weight : 'primary',
4914                 html : 'Yes'
4915             }
4916         ],
4917
4918         /**
4919          * Button config that displays OK and Cancel buttons
4920          * @type Object
4921          */
4922         OKCANCEL : [
4923             {
4924                name : 'cancel',
4925                 html : 'Cancel'
4926             },
4927             {
4928                 name : 'ok',
4929                 weight : 'primary',
4930                 html : 'OK'
4931             }
4932         ],
4933         /**
4934          * Button config that displays Yes, No and Cancel buttons
4935          * @type Object
4936          */
4937         YESNOCANCEL : [
4938             {
4939                 name : 'yes',
4940                 weight : 'primary',
4941                 html : 'Yes'
4942             },
4943             {
4944                 name : 'no',
4945                 html : 'No'
4946             },
4947             {
4948                 name : 'cancel',
4949                 html : 'Cancel'
4950             }
4951         ],
4952         
4953         zIndex : 10001
4954 });
4955
4956 /*
4957  * - LGPL
4958  *
4959  * messagebox - can be used as a replace
4960  * 
4961  */
4962 /**
4963  * @class Roo.MessageBox
4964  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4965  * Example usage:
4966  *<pre><code>
4967 // Basic alert:
4968 Roo.Msg.alert('Status', 'Changes saved successfully.');
4969
4970 // Prompt for user data:
4971 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4972     if (btn == 'ok'){
4973         // process text value...
4974     }
4975 });
4976
4977 // Show a dialog using config options:
4978 Roo.Msg.show({
4979    title:'Save Changes?',
4980    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4981    buttons: Roo.Msg.YESNOCANCEL,
4982    fn: processResult,
4983    animEl: 'elId'
4984 });
4985 </code></pre>
4986  * @singleton
4987  */
4988 Roo.bootstrap.MessageBox = function(){
4989     var dlg, opt, mask, waitTimer;
4990     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4991     var buttons, activeTextEl, bwidth;
4992
4993     
4994     // private
4995     var handleButton = function(button){
4996         dlg.hide();
4997         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4998     };
4999
5000     // private
5001     var handleHide = function(){
5002         if(opt && opt.cls){
5003             dlg.el.removeClass(opt.cls);
5004         }
5005         //if(waitTimer){
5006         //    Roo.TaskMgr.stop(waitTimer);
5007         //    waitTimer = null;
5008         //}
5009     };
5010
5011     // private
5012     var updateButtons = function(b){
5013         var width = 0;
5014         if(!b){
5015             buttons["ok"].hide();
5016             buttons["cancel"].hide();
5017             buttons["yes"].hide();
5018             buttons["no"].hide();
5019             dlg.footerEl.hide();
5020             
5021             return width;
5022         }
5023         dlg.footerEl.show();
5024         for(var k in buttons){
5025             if(typeof buttons[k] != "function"){
5026                 if(b[k]){
5027                     buttons[k].show();
5028                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5029                     width += buttons[k].el.getWidth()+15;
5030                 }else{
5031                     buttons[k].hide();
5032                 }
5033             }
5034         }
5035         return width;
5036     };
5037
5038     // private
5039     var handleEsc = function(d, k, e){
5040         if(opt && opt.closable !== false){
5041             dlg.hide();
5042         }
5043         if(e){
5044             e.stopEvent();
5045         }
5046     };
5047
5048     return {
5049         /**
5050          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5051          * @return {Roo.BasicDialog} The BasicDialog element
5052          */
5053         getDialog : function(){
5054            if(!dlg){
5055                 dlg = new Roo.bootstrap.Modal( {
5056                     //draggable: true,
5057                     //resizable:false,
5058                     //constraintoviewport:false,
5059                     //fixedcenter:true,
5060                     //collapsible : false,
5061                     //shim:true,
5062                     //modal: true,
5063                 //    width: 'auto',
5064                   //  height:100,
5065                     //buttonAlign:"center",
5066                     closeClick : function(){
5067                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5068                             handleButton("no");
5069                         }else{
5070                             handleButton("cancel");
5071                         }
5072                     }
5073                 });
5074                 dlg.render();
5075                 dlg.on("hide", handleHide);
5076                 mask = dlg.mask;
5077                 //dlg.addKeyListener(27, handleEsc);
5078                 buttons = {};
5079                 this.buttons = buttons;
5080                 var bt = this.buttonText;
5081                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5082                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5083                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5084                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5085                 //Roo.log(buttons);
5086                 bodyEl = dlg.bodyEl.createChild({
5087
5088                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5089                         '<textarea class="roo-mb-textarea"></textarea>' +
5090                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5091                 });
5092                 msgEl = bodyEl.dom.firstChild;
5093                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5094                 textboxEl.enableDisplayMode();
5095                 textboxEl.addKeyListener([10,13], function(){
5096                     if(dlg.isVisible() && opt && opt.buttons){
5097                         if(opt.buttons.ok){
5098                             handleButton("ok");
5099                         }else if(opt.buttons.yes){
5100                             handleButton("yes");
5101                         }
5102                     }
5103                 });
5104                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5105                 textareaEl.enableDisplayMode();
5106                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5107                 progressEl.enableDisplayMode();
5108                 
5109                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5110                 var pf = progressEl.dom.firstChild;
5111                 if (pf) {
5112                     pp = Roo.get(pf.firstChild);
5113                     pp.setHeight(pf.offsetHeight);
5114                 }
5115                 
5116             }
5117             return dlg;
5118         },
5119
5120         /**
5121          * Updates the message box body text
5122          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5123          * the XHTML-compliant non-breaking space character '&amp;#160;')
5124          * @return {Roo.MessageBox} This message box
5125          */
5126         updateText : function(text)
5127         {
5128             if(!dlg.isVisible() && !opt.width){
5129                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5130                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5131             }
5132             msgEl.innerHTML = text || '&#160;';
5133       
5134             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5135             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5136             var w = Math.max(
5137                     Math.min(opt.width || cw , this.maxWidth), 
5138                     Math.max(opt.minWidth || this.minWidth, bwidth)
5139             );
5140             if(opt.prompt){
5141                 activeTextEl.setWidth(w);
5142             }
5143             if(dlg.isVisible()){
5144                 dlg.fixedcenter = false;
5145             }
5146             // to big, make it scroll. = But as usual stupid IE does not support
5147             // !important..
5148             
5149             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5150                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5151                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5152             } else {
5153                 bodyEl.dom.style.height = '';
5154                 bodyEl.dom.style.overflowY = '';
5155             }
5156             if (cw > w) {
5157                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5158             } else {
5159                 bodyEl.dom.style.overflowX = '';
5160             }
5161             
5162             dlg.setContentSize(w, bodyEl.getHeight());
5163             if(dlg.isVisible()){
5164                 dlg.fixedcenter = true;
5165             }
5166             return this;
5167         },
5168
5169         /**
5170          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5171          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5172          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5173          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5174          * @return {Roo.MessageBox} This message box
5175          */
5176         updateProgress : function(value, text){
5177             if(text){
5178                 this.updateText(text);
5179             }
5180             
5181             if (pp) { // weird bug on my firefox - for some reason this is not defined
5182                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5183                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5184             }
5185             return this;
5186         },        
5187
5188         /**
5189          * Returns true if the message box is currently displayed
5190          * @return {Boolean} True if the message box is visible, else false
5191          */
5192         isVisible : function(){
5193             return dlg && dlg.isVisible();  
5194         },
5195
5196         /**
5197          * Hides the message box if it is displayed
5198          */
5199         hide : function(){
5200             if(this.isVisible()){
5201                 dlg.hide();
5202             }  
5203         },
5204
5205         /**
5206          * Displays a new message box, or reinitializes an existing message box, based on the config options
5207          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5208          * The following config object properties are supported:
5209          * <pre>
5210 Property    Type             Description
5211 ----------  ---------------  ------------------------------------------------------------------------------------
5212 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5213                                    closes (defaults to undefined)
5214 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5215                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5216 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5217                                    progress and wait dialogs will ignore this property and always hide the
5218                                    close button as they can only be closed programmatically.
5219 cls               String           A custom CSS class to apply to the message box element
5220 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5221                                    displayed (defaults to 75)
5222 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5223                                    function will be btn (the name of the button that was clicked, if applicable,
5224                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5225                                    Progress and wait dialogs will ignore this option since they do not respond to
5226                                    user actions and can only be closed programmatically, so any required function
5227                                    should be called by the same code after it closes the dialog.
5228 icon              String           A CSS class that provides a background image to be used as an icon for
5229                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5230 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5231 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5232 modal             Boolean          False to allow user interaction with the page while the message box is
5233                                    displayed (defaults to true)
5234 msg               String           A string that will replace the existing message box body text (defaults
5235                                    to the XHTML-compliant non-breaking space character '&#160;')
5236 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5237 progress          Boolean          True to display a progress bar (defaults to false)
5238 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5239 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5240 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5241 title             String           The title text
5242 value             String           The string value to set into the active textbox element if displayed
5243 wait              Boolean          True to display a progress bar (defaults to false)
5244 width             Number           The width of the dialog in pixels
5245 </pre>
5246          *
5247          * Example usage:
5248          * <pre><code>
5249 Roo.Msg.show({
5250    title: 'Address',
5251    msg: 'Please enter your address:',
5252    width: 300,
5253    buttons: Roo.MessageBox.OKCANCEL,
5254    multiline: true,
5255    fn: saveAddress,
5256    animEl: 'addAddressBtn'
5257 });
5258 </code></pre>
5259          * @param {Object} config Configuration options
5260          * @return {Roo.MessageBox} This message box
5261          */
5262         show : function(options)
5263         {
5264             
5265             // this causes nightmares if you show one dialog after another
5266             // especially on callbacks..
5267              
5268             if(this.isVisible()){
5269                 
5270                 this.hide();
5271                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5272                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5273                 Roo.log("New Dialog Message:" +  options.msg )
5274                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5275                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5276                 
5277             }
5278             var d = this.getDialog();
5279             opt = options;
5280             d.setTitle(opt.title || "&#160;");
5281             d.closeEl.setDisplayed(opt.closable !== false);
5282             activeTextEl = textboxEl;
5283             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5284             if(opt.prompt){
5285                 if(opt.multiline){
5286                     textboxEl.hide();
5287                     textareaEl.show();
5288                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5289                         opt.multiline : this.defaultTextHeight);
5290                     activeTextEl = textareaEl;
5291                 }else{
5292                     textboxEl.show();
5293                     textareaEl.hide();
5294                 }
5295             }else{
5296                 textboxEl.hide();
5297                 textareaEl.hide();
5298             }
5299             progressEl.setDisplayed(opt.progress === true);
5300             if (opt.progress) {
5301                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5302             }
5303             this.updateProgress(0);
5304             activeTextEl.dom.value = opt.value || "";
5305             if(opt.prompt){
5306                 dlg.setDefaultButton(activeTextEl);
5307             }else{
5308                 var bs = opt.buttons;
5309                 var db = null;
5310                 if(bs && bs.ok){
5311                     db = buttons["ok"];
5312                 }else if(bs && bs.yes){
5313                     db = buttons["yes"];
5314                 }
5315                 dlg.setDefaultButton(db);
5316             }
5317             bwidth = updateButtons(opt.buttons);
5318             this.updateText(opt.msg);
5319             if(opt.cls){
5320                 d.el.addClass(opt.cls);
5321             }
5322             d.proxyDrag = opt.proxyDrag === true;
5323             d.modal = opt.modal !== false;
5324             d.mask = opt.modal !== false ? mask : false;
5325             if(!d.isVisible()){
5326                 // force it to the end of the z-index stack so it gets a cursor in FF
5327                 document.body.appendChild(dlg.el.dom);
5328                 d.animateTarget = null;
5329                 d.show(options.animEl);
5330             }
5331             return this;
5332         },
5333
5334         /**
5335          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5336          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5337          * and closing the message box when the process is complete.
5338          * @param {String} title The title bar text
5339          * @param {String} msg The message box body text
5340          * @return {Roo.MessageBox} This message box
5341          */
5342         progress : function(title, msg){
5343             this.show({
5344                 title : title,
5345                 msg : msg,
5346                 buttons: false,
5347                 progress:true,
5348                 closable:false,
5349                 minWidth: this.minProgressWidth,
5350                 modal : true
5351             });
5352             return this;
5353         },
5354
5355         /**
5356          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5357          * If a callback function is passed it will be called after the user clicks the button, and the
5358          * id of the button that was clicked will be passed as the only parameter to the callback
5359          * (could also be the top-right close button).
5360          * @param {String} title The title bar text
5361          * @param {String} msg The message box body text
5362          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5363          * @param {Object} scope (optional) The scope of the callback function
5364          * @return {Roo.MessageBox} This message box
5365          */
5366         alert : function(title, msg, fn, scope)
5367         {
5368             this.show({
5369                 title : title,
5370                 msg : msg,
5371                 buttons: this.OK,
5372                 fn: fn,
5373                 closable : false,
5374                 scope : scope,
5375                 modal : true
5376             });
5377             return this;
5378         },
5379
5380         /**
5381          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5382          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5383          * You are responsible for closing the message box when the process is complete.
5384          * @param {String} msg The message box body text
5385          * @param {String} title (optional) The title bar text
5386          * @return {Roo.MessageBox} This message box
5387          */
5388         wait : function(msg, title){
5389             this.show({
5390                 title : title,
5391                 msg : msg,
5392                 buttons: false,
5393                 closable:false,
5394                 progress:true,
5395                 modal:true,
5396                 width:300,
5397                 wait:true
5398             });
5399             waitTimer = Roo.TaskMgr.start({
5400                 run: function(i){
5401                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5402                 },
5403                 interval: 1000
5404             });
5405             return this;
5406         },
5407
5408         /**
5409          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5410          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5411          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5412          * @param {String} title The title bar text
5413          * @param {String} msg The message box body text
5414          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5415          * @param {Object} scope (optional) The scope of the callback function
5416          * @return {Roo.MessageBox} This message box
5417          */
5418         confirm : function(title, msg, fn, scope){
5419             this.show({
5420                 title : title,
5421                 msg : msg,
5422                 buttons: this.YESNO,
5423                 fn: fn,
5424                 scope : scope,
5425                 modal : true
5426             });
5427             return this;
5428         },
5429
5430         /**
5431          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5432          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5433          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5434          * (could also be the top-right close button) and the text that was entered will be passed as the two
5435          * parameters to the callback.
5436          * @param {String} title The title bar text
5437          * @param {String} msg The message box body text
5438          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5439          * @param {Object} scope (optional) The scope of the callback function
5440          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5441          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5442          * @return {Roo.MessageBox} This message box
5443          */
5444         prompt : function(title, msg, fn, scope, multiline){
5445             this.show({
5446                 title : title,
5447                 msg : msg,
5448                 buttons: this.OKCANCEL,
5449                 fn: fn,
5450                 minWidth:250,
5451                 scope : scope,
5452                 prompt:true,
5453                 multiline: multiline,
5454                 modal : true
5455             });
5456             return this;
5457         },
5458
5459         /**
5460          * Button config that displays a single OK button
5461          * @type Object
5462          */
5463         OK : {ok:true},
5464         /**
5465          * Button config that displays Yes and No buttons
5466          * @type Object
5467          */
5468         YESNO : {yes:true, no:true},
5469         /**
5470          * Button config that displays OK and Cancel buttons
5471          * @type Object
5472          */
5473         OKCANCEL : {ok:true, cancel:true},
5474         /**
5475          * Button config that displays Yes, No and Cancel buttons
5476          * @type Object
5477          */
5478         YESNOCANCEL : {yes:true, no:true, cancel:true},
5479
5480         /**
5481          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5482          * @type Number
5483          */
5484         defaultTextHeight : 75,
5485         /**
5486          * The maximum width in pixels of the message box (defaults to 600)
5487          * @type Number
5488          */
5489         maxWidth : 600,
5490         /**
5491          * The minimum width in pixels of the message box (defaults to 100)
5492          * @type Number
5493          */
5494         minWidth : 100,
5495         /**
5496          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5497          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5498          * @type Number
5499          */
5500         minProgressWidth : 250,
5501         /**
5502          * An object containing the default button text strings that can be overriden for localized language support.
5503          * Supported properties are: ok, cancel, yes and no.
5504          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5505          * @type Object
5506          */
5507         buttonText : {
5508             ok : "OK",
5509             cancel : "Cancel",
5510             yes : "Yes",
5511             no : "No"
5512         }
5513     };
5514 }();
5515
5516 /**
5517  * Shorthand for {@link Roo.MessageBox}
5518  */
5519 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5520 Roo.Msg = Roo.Msg || Roo.MessageBox;
5521 /*
5522  * - LGPL
5523  *
5524  * navbar
5525  * 
5526  */
5527
5528 /**
5529  * @class Roo.bootstrap.Navbar
5530  * @extends Roo.bootstrap.Component
5531  * Bootstrap Navbar class
5532
5533  * @constructor
5534  * Create a new Navbar
5535  * @param {Object} config The config object
5536  */
5537
5538
5539 Roo.bootstrap.Navbar = function(config){
5540     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5541     this.addEvents({
5542         // raw events
5543         /**
5544          * @event beforetoggle
5545          * Fire before toggle the menu
5546          * @param {Roo.EventObject} e
5547          */
5548         "beforetoggle" : true
5549     });
5550 };
5551
5552 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5553     
5554     
5555    
5556     // private
5557     navItems : false,
5558     loadMask : false,
5559     
5560     
5561     getAutoCreate : function(){
5562         
5563         
5564         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5565         
5566     },
5567     
5568     initEvents :function ()
5569     {
5570         //Roo.log(this.el.select('.navbar-toggle',true));
5571         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5572         
5573         var mark = {
5574             tag: "div",
5575             cls:"x-dlg-mask"
5576         };
5577         
5578         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5579         
5580         var size = this.el.getSize();
5581         this.maskEl.setSize(size.width, size.height);
5582         this.maskEl.enableDisplayMode("block");
5583         this.maskEl.hide();
5584         
5585         if(this.loadMask){
5586             this.maskEl.show();
5587         }
5588     },
5589     
5590     
5591     getChildContainer : function()
5592     {
5593         if (this.el && this.el.select('.collapse').getCount()) {
5594             return this.el.select('.collapse',true).first();
5595         }
5596         
5597         return this.el;
5598     },
5599     
5600     mask : function()
5601     {
5602         this.maskEl.show();
5603     },
5604     
5605     unmask : function()
5606     {
5607         this.maskEl.hide();
5608     },
5609     onToggle : function()
5610     {
5611         
5612         if(this.fireEvent('beforetoggle', this) === false){
5613             return;
5614         }
5615         var ce = this.el.select('.navbar-collapse',true).first();
5616       
5617         if (!ce.hasClass('show')) {
5618            this.expand();
5619         } else {
5620             this.collapse();
5621         }
5622         
5623         
5624     
5625     },
5626     /**
5627      * Expand the navbar pulldown 
5628      */
5629     expand : function ()
5630     {
5631        
5632         var ce = this.el.select('.navbar-collapse',true).first();
5633         if (ce.hasClass('collapsing')) {
5634             return;
5635         }
5636         ce.dom.style.height = '';
5637                // show it...
5638         ce.addClass('in'); // old...
5639         ce.removeClass('collapse');
5640         ce.addClass('show');
5641         var h = ce.getHeight();
5642         Roo.log(h);
5643         ce.removeClass('show');
5644         // at this point we should be able to see it..
5645         ce.addClass('collapsing');
5646         
5647         ce.setHeight(0); // resize it ...
5648         ce.on('transitionend', function() {
5649             //Roo.log('done transition');
5650             ce.removeClass('collapsing');
5651             ce.addClass('show');
5652             ce.removeClass('collapse');
5653
5654             ce.dom.style.height = '';
5655         }, this, { single: true} );
5656         ce.setHeight(h);
5657         ce.dom.scrollTop = 0;
5658     },
5659     /**
5660      * Collapse the navbar pulldown 
5661      */
5662     collapse : function()
5663     {
5664          var ce = this.el.select('.navbar-collapse',true).first();
5665        
5666         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5667             // it's collapsed or collapsing..
5668             return;
5669         }
5670         ce.removeClass('in'); // old...
5671         ce.setHeight(ce.getHeight());
5672         ce.removeClass('show');
5673         ce.addClass('collapsing');
5674         
5675         ce.on('transitionend', function() {
5676             ce.dom.style.height = '';
5677             ce.removeClass('collapsing');
5678             ce.addClass('collapse');
5679         }, this, { single: true} );
5680         ce.setHeight(0);
5681     }
5682     
5683     
5684     
5685 });
5686
5687
5688
5689  
5690
5691  /*
5692  * - LGPL
5693  *
5694  * navbar
5695  * 
5696  */
5697
5698 /**
5699  * @class Roo.bootstrap.NavSimplebar
5700  * @extends Roo.bootstrap.Navbar
5701  * Bootstrap Sidebar class
5702  *
5703  * @cfg {Boolean} inverse is inverted color
5704  * 
5705  * @cfg {String} type (nav | pills | tabs)
5706  * @cfg {Boolean} arrangement stacked | justified
5707  * @cfg {String} align (left | right) alignment
5708  * 
5709  * @cfg {Boolean} main (true|false) main nav bar? default false
5710  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5711  * 
5712  * @cfg {String} tag (header|footer|nav|div) default is nav 
5713
5714  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5715  * 
5716  * 
5717  * @constructor
5718  * Create a new Sidebar
5719  * @param {Object} config The config object
5720  */
5721
5722
5723 Roo.bootstrap.NavSimplebar = function(config){
5724     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5725 };
5726
5727 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5728     
5729     inverse: false,
5730     
5731     type: false,
5732     arrangement: '',
5733     align : false,
5734     
5735     weight : 'light',
5736     
5737     main : false,
5738     
5739     
5740     tag : false,
5741     
5742     
5743     getAutoCreate : function(){
5744         
5745         
5746         var cfg = {
5747             tag : this.tag || 'div',
5748             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5749         };
5750         if (['light','white'].indexOf(this.weight) > -1) {
5751             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5752         }
5753         cfg.cls += ' bg-' + this.weight;
5754         
5755         if (this.inverse) {
5756             cfg.cls += ' navbar-inverse';
5757             
5758         }
5759         
5760         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5761         
5762         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5763             return cfg;
5764         }
5765         
5766         
5767     
5768         
5769         cfg.cn = [
5770             {
5771                 cls: 'nav nav-' + this.xtype,
5772                 tag : 'ul'
5773             }
5774         ];
5775         
5776          
5777         this.type = this.type || 'nav';
5778         if (['tabs','pills'].indexOf(this.type) != -1) {
5779             cfg.cn[0].cls += ' nav-' + this.type
5780         
5781         
5782         } else {
5783             if (this.type!=='nav') {
5784                 Roo.log('nav type must be nav/tabs/pills')
5785             }
5786             cfg.cn[0].cls += ' navbar-nav'
5787         }
5788         
5789         
5790         
5791         
5792         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5793             cfg.cn[0].cls += ' nav-' + this.arrangement;
5794         }
5795         
5796         
5797         if (this.align === 'right') {
5798             cfg.cn[0].cls += ' navbar-right';
5799         }
5800         
5801         
5802         
5803         
5804         return cfg;
5805     
5806         
5807     }
5808     
5809     
5810     
5811 });
5812
5813
5814
5815  
5816
5817  
5818        /*
5819  * - LGPL
5820  *
5821  * navbar
5822  * navbar-fixed-top
5823  * navbar-expand-md  fixed-top 
5824  */
5825
5826 /**
5827  * @class Roo.bootstrap.NavHeaderbar
5828  * @extends Roo.bootstrap.NavSimplebar
5829  * Bootstrap Sidebar class
5830  *
5831  * @cfg {String} brand what is brand
5832  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5833  * @cfg {String} brand_href href of the brand
5834  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5835  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5836  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5837  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5838  * 
5839  * @constructor
5840  * Create a new Sidebar
5841  * @param {Object} config The config object
5842  */
5843
5844
5845 Roo.bootstrap.NavHeaderbar = function(config){
5846     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5847       
5848 };
5849
5850 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5851     
5852     position: '',
5853     brand: '',
5854     brand_href: false,
5855     srButton : true,
5856     autohide : false,
5857     desktopCenter : false,
5858    
5859     
5860     getAutoCreate : function(){
5861         
5862         var   cfg = {
5863             tag: this.nav || 'nav',
5864             cls: 'navbar navbar-expand-md',
5865             role: 'navigation',
5866             cn: []
5867         };
5868         
5869         var cn = cfg.cn;
5870         if (this.desktopCenter) {
5871             cn.push({cls : 'container', cn : []});
5872             cn = cn[0].cn;
5873         }
5874         
5875         if(this.srButton){
5876             var btn = {
5877                 tag: 'button',
5878                 type: 'button',
5879                 cls: 'navbar-toggle navbar-toggler',
5880                 'data-toggle': 'collapse',
5881                 cn: [
5882                     {
5883                         tag: 'span',
5884                         cls: 'sr-only',
5885                         html: 'Toggle navigation'
5886                     },
5887                     {
5888                         tag: 'span',
5889                         cls: 'icon-bar navbar-toggler-icon'
5890                     },
5891                     {
5892                         tag: 'span',
5893                         cls: 'icon-bar'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar'
5898                     }
5899                 ]
5900             };
5901             
5902             cn.push( Roo.bootstrap.version == 4 ? btn : {
5903                 tag: 'div',
5904                 cls: 'navbar-header',
5905                 cn: [
5906                     btn
5907                 ]
5908             });
5909         }
5910         
5911         cn.push({
5912             tag: 'div',
5913             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5914             cn : []
5915         });
5916         
5917         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5918         
5919         if (['light','white'].indexOf(this.weight) > -1) {
5920             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5921         }
5922         cfg.cls += ' bg-' + this.weight;
5923         
5924         
5925         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5926             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5927             
5928             // tag can override this..
5929             
5930             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5931         }
5932         
5933         if (this.brand !== '') {
5934             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5935             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5936                 tag: 'a',
5937                 href: this.brand_href ? this.brand_href : '#',
5938                 cls: 'navbar-brand',
5939                 cn: [
5940                 this.brand
5941                 ]
5942             });
5943         }
5944         
5945         if(this.main){
5946             cfg.cls += ' main-nav';
5947         }
5948         
5949         
5950         return cfg;
5951
5952         
5953     },
5954     getHeaderChildContainer : function()
5955     {
5956         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5957             return this.el.select('.navbar-header',true).first();
5958         }
5959         
5960         return this.getChildContainer();
5961     },
5962     
5963     getChildContainer : function()
5964     {
5965          
5966         return this.el.select('.roo-navbar-collapse',true).first();
5967          
5968         
5969     },
5970     
5971     initEvents : function()
5972     {
5973         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5974         
5975         if (this.autohide) {
5976             
5977             var prevScroll = 0;
5978             var ft = this.el;
5979             
5980             Roo.get(document).on('scroll',function(e) {
5981                 var ns = Roo.get(document).getScroll().top;
5982                 var os = prevScroll;
5983                 prevScroll = ns;
5984                 
5985                 if(ns > os){
5986                     ft.removeClass('slideDown');
5987                     ft.addClass('slideUp');
5988                     return;
5989                 }
5990                 ft.removeClass('slideUp');
5991                 ft.addClass('slideDown');
5992                  
5993               
5994           },this);
5995         }
5996     }    
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * navbar
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.NavSidebar
6013  * @extends Roo.bootstrap.Navbar
6014  * Bootstrap Sidebar class
6015  * 
6016  * @constructor
6017  * Create a new Sidebar
6018  * @param {Object} config The config object
6019  */
6020
6021
6022 Roo.bootstrap.NavSidebar = function(config){
6023     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6024 };
6025
6026 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6027     
6028     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6029     
6030     getAutoCreate : function(){
6031         
6032         
6033         return  {
6034             tag: 'div',
6035             cls: 'sidebar sidebar-nav'
6036         };
6037     
6038         
6039     }
6040     
6041     
6042     
6043 });
6044
6045
6046
6047  
6048
6049  /*
6050  * - LGPL
6051  *
6052  * nav group
6053  * 
6054  */
6055
6056 /**
6057  * @class Roo.bootstrap.NavGroup
6058  * @extends Roo.bootstrap.Component
6059  * Bootstrap NavGroup class
6060  * @cfg {String} align (left|right)
6061  * @cfg {Boolean} inverse
6062  * @cfg {String} type (nav|pills|tab) default nav
6063  * @cfg {String} navId - reference Id for navbar.
6064  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6065  * 
6066  * @constructor
6067  * Create a new nav group
6068  * @param {Object} config The config object
6069  */
6070
6071 Roo.bootstrap.NavGroup = function(config){
6072     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6073     this.navItems = [];
6074    
6075     Roo.bootstrap.NavGroup.register(this);
6076      this.addEvents({
6077         /**
6078              * @event changed
6079              * Fires when the active item changes
6080              * @param {Roo.bootstrap.NavGroup} this
6081              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6083          */
6084         'changed': true
6085      });
6086     
6087 };
6088
6089 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6090     
6091     align: '',
6092     inverse: false,
6093     form: false,
6094     type: 'nav',
6095     navId : '',
6096     // private
6097     pilltype : true,
6098     
6099     navItems : false, 
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag : 'ul',
6107             cls: 'nav' 
6108         };
6109         if (Roo.bootstrap.version == 4) {
6110             if (['tabs','pills'].indexOf(this.type) != -1) {
6111                 cfg.cls += ' nav-' + this.type; 
6112             } else {
6113                 // trying to remove so header bar can right align top?
6114                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115                     // do not use on header bar... 
6116                     cfg.cls += ' navbar-nav';
6117                 }
6118             }
6119             
6120         } else {
6121             if (['tabs','pills'].indexOf(this.type) != -1) {
6122                 cfg.cls += ' nav-' + this.type
6123             } else {
6124                 if (this.type !== 'nav') {
6125                     Roo.log('nav type must be nav/tabs/pills')
6126                 }
6127                 cfg.cls += ' navbar-nav'
6128             }
6129         }
6130         
6131         if (this.parent() && this.parent().sidebar) {
6132             cfg = {
6133                 tag: 'ul',
6134                 cls: 'dashboard-menu sidebar-menu'
6135             };
6136             
6137             return cfg;
6138         }
6139         
6140         if (this.form === true) {
6141             cfg = {
6142                 tag: 'form',
6143                 cls: 'navbar-form form-inline'
6144             };
6145             //nav navbar-right ml-md-auto
6146             if (this.align === 'right') {
6147                 cfg.cls += ' navbar-right ml-md-auto';
6148             } else {
6149                 cfg.cls += ' navbar-left';
6150             }
6151         }
6152         
6153         if (this.align === 'right') {
6154             cfg.cls += ' navbar-right ml-md-auto';
6155         } else {
6156             cfg.cls += ' mr-auto';
6157         }
6158         
6159         if (this.inverse) {
6160             cfg.cls += ' navbar-inverse';
6161             
6162         }
6163         
6164         
6165         return cfg;
6166     },
6167     /**
6168     * sets the active Navigation item
6169     * @param {Roo.bootstrap.NavItem} the new current navitem
6170     */
6171     setActiveItem : function(item)
6172     {
6173         var prev = false;
6174         Roo.each(this.navItems, function(v){
6175             if (v == item) {
6176                 return ;
6177             }
6178             if (v.isActive()) {
6179                 v.setActive(false, true);
6180                 prev = v;
6181                 
6182             }
6183             
6184         });
6185
6186         item.setActive(true, true);
6187         this.fireEvent('changed', this, item, prev);
6188         
6189         
6190     },
6191     /**
6192     * gets the active Navigation item
6193     * @return {Roo.bootstrap.NavItem} the current navitem
6194     */
6195     getActive : function()
6196     {
6197         
6198         var prev = false;
6199         Roo.each(this.navItems, function(v){
6200             
6201             if (v.isActive()) {
6202                 prev = v;
6203                 
6204             }
6205             
6206         });
6207         return prev;
6208     },
6209     
6210     indexOfNav : function()
6211     {
6212         
6213         var prev = false;
6214         Roo.each(this.navItems, function(v,i){
6215             
6216             if (v.isActive()) {
6217                 prev = i;
6218                 
6219             }
6220             
6221         });
6222         return prev;
6223     },
6224     /**
6225     * adds a Navigation item
6226     * @param {Roo.bootstrap.NavItem} the navitem to add
6227     */
6228     addItem : function(cfg)
6229     {
6230         if (this.form && Roo.bootstrap.version == 4) {
6231             cfg.tag = 'div';
6232         }
6233         var cn = new Roo.bootstrap.NavItem(cfg);
6234         this.register(cn);
6235         cn.parentId = this.id;
6236         cn.onRender(this.el, null);
6237         return cn;
6238     },
6239     /**
6240     * register a Navigation item
6241     * @param {Roo.bootstrap.NavItem} the navitem to add
6242     */
6243     register : function(item)
6244     {
6245         this.navItems.push( item);
6246         item.navId = this.navId;
6247     
6248     },
6249     
6250     /**
6251     * clear all the Navigation item
6252     */
6253    
6254     clearAll : function()
6255     {
6256         this.navItems = [];
6257         this.el.dom.innerHTML = '';
6258     },
6259     
6260     getNavItem: function(tabId)
6261     {
6262         var ret = false;
6263         Roo.each(this.navItems, function(e) {
6264             if (e.tabId == tabId) {
6265                ret =  e;
6266                return false;
6267             }
6268             return true;
6269             
6270         });
6271         return ret;
6272     },
6273     
6274     setActiveNext : function()
6275     {
6276         var i = this.indexOfNav(this.getActive());
6277         if (i > this.navItems.length) {
6278             return;
6279         }
6280         this.setActiveItem(this.navItems[i+1]);
6281     },
6282     setActivePrev : function()
6283     {
6284         var i = this.indexOfNav(this.getActive());
6285         if (i  < 1) {
6286             return;
6287         }
6288         this.setActiveItem(this.navItems[i-1]);
6289     },
6290     clearWasActive : function(except) {
6291         Roo.each(this.navItems, function(e) {
6292             if (e.tabId != except.tabId && e.was_active) {
6293                e.was_active = false;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299     },
6300     getWasActive : function ()
6301     {
6302         var r = false;
6303         Roo.each(this.navItems, function(e) {
6304             if (e.was_active) {
6305                r = e;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311         return r;
6312     }
6313     
6314     
6315 });
6316
6317  
6318 Roo.apply(Roo.bootstrap.NavGroup, {
6319     
6320     groups: {},
6321      /**
6322     * register a Navigation Group
6323     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6324     */
6325     register : function(navgrp)
6326     {
6327         this.groups[navgrp.navId] = navgrp;
6328         
6329     },
6330     /**
6331     * fetch a Navigation Group based on the navigation ID
6332     * @param {string} the navgroup to add
6333     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6334     */
6335     get: function(navId) {
6336         if (typeof(this.groups[navId]) == 'undefined') {
6337             return false;
6338             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6339         }
6340         return this.groups[navId] ;
6341     }
6342     
6343     
6344     
6345 });
6346
6347  /*
6348  * - LGPL
6349  *
6350  * row
6351  * 
6352  */
6353
6354 /**
6355  * @class Roo.bootstrap.NavItem
6356  * @extends Roo.bootstrap.Component
6357  * Bootstrap Navbar.NavItem class
6358  * @cfg {String} href  link to
6359  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6360  * @cfg {Boolean} button_outline show and outlined button
6361  * @cfg {String} html content of button
6362  * @cfg {String} badge text inside badge
6363  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6364  * @cfg {String} glyphicon DEPRICATED - use fa
6365  * @cfg {String} icon DEPRICATED - use fa
6366  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6367  * @cfg {Boolean} active Is item active
6368  * @cfg {Boolean} disabled Is item disabled
6369  * @cfg {String} linkcls  Link Class
6370  * @cfg {Boolean} preventDefault (true | false) default false
6371  * @cfg {String} tabId the tab that this item activates.
6372  * @cfg {String} tagtype (a|span) render as a href or span?
6373  * @cfg {Boolean} animateRef (true|false) link to element default false  
6374   
6375  * @constructor
6376  * Create a new Navbar Item
6377  * @param {Object} config The config object
6378  */
6379 Roo.bootstrap.NavItem = function(config){
6380     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6381     this.addEvents({
6382         // raw events
6383         /**
6384          * @event click
6385          * The raw click event for the entire grid.
6386          * @param {Roo.EventObject} e
6387          */
6388         "click" : true,
6389          /**
6390             * @event changed
6391             * Fires when the active item active state changes
6392             * @param {Roo.bootstrap.NavItem} this
6393             * @param {boolean} state the new state
6394              
6395          */
6396         'changed': true,
6397         /**
6398             * @event scrollto
6399             * Fires when scroll to element
6400             * @param {Roo.bootstrap.NavItem} this
6401             * @param {Object} options
6402             * @param {Roo.EventObject} e
6403              
6404          */
6405         'scrollto': true
6406     });
6407    
6408 };
6409
6410 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6411     
6412     href: false,
6413     html: '',
6414     badge: '',
6415     icon: false,
6416     fa : false,
6417     glyphicon: false,
6418     active: false,
6419     preventDefault : false,
6420     tabId : false,
6421     tagtype : 'a',
6422     tag: 'li',
6423     disabled : false,
6424     animateRef : false,
6425     was_active : false,
6426     button_weight : '',
6427     button_outline : false,
6428     linkcls : '',
6429     navLink: false,
6430     
6431     getAutoCreate : function(){
6432          
6433         var cfg = {
6434             tag: this.tag,
6435             cls: 'nav-item'
6436         };
6437         
6438         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6439         
6440         if (this.active) {
6441             cfg.cls +=  ' active' ;
6442         }
6443         if (this.disabled) {
6444             cfg.cls += ' disabled';
6445         }
6446         
6447         // BS4 only?
6448         if (this.button_weight.length) {
6449             cfg.tag = this.href ? 'a' : 'button';
6450             cfg.html = this.html || '';
6451             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6452             if (this.href) {
6453                 cfg.href = this.href;
6454             }
6455             if (this.fa) {
6456                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6457             } else {
6458                 cfg.cls += " nav-html";
6459             }
6460             
6461             // menu .. should add dropdown-menu class - so no need for carat..
6462             
6463             if (this.badge !== '') {
6464                  
6465                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6466             }
6467             return cfg;
6468         }
6469         
6470         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6471             cfg.cn = [
6472                 {
6473                     tag: this.tagtype,
6474                     href : this.href || "#",
6475                     html: this.html || '',
6476                     cls : ''
6477                 }
6478             ];
6479             if (this.tagtype == 'a') {
6480                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6481         
6482             }
6483             if (this.icon) {
6484                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else  if (this.fa) {
6486                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6487             } else if(this.glyphicon) {
6488                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6489             } else {
6490                 cfg.cn[0].cls += " nav-html";
6491             }
6492             
6493             if (this.menu) {
6494                 cfg.cn[0].html += " <span class='caret'></span>";
6495              
6496             }
6497             
6498             if (this.badge !== '') {
6499                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6500             }
6501         }
6502         
6503         
6504         
6505         return cfg;
6506     },
6507     onRender : function(ct, position)
6508     {
6509        // Roo.log("Call onRender: " + this.xtype);
6510         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6511             this.tag = 'div';
6512         }
6513         
6514         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6515         this.navLink = this.el.select('.nav-link',true).first();
6516         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6517         return ret;
6518     },
6519       
6520     
6521     initEvents: function() 
6522     {
6523         if (typeof (this.menu) != 'undefined') {
6524             this.menu.parentType = this.xtype;
6525             this.menu.triggerEl = this.el;
6526             this.menu = this.addxtype(Roo.apply({}, this.menu));
6527         }
6528         
6529         this.el.on('click', this.onClick, this);
6530         
6531         //if(this.tagtype == 'span'){
6532         //    this.el.select('span',true).on('click', this.onClick, this);
6533         //}
6534        
6535         // at this point parent should be available..
6536         this.parent().register(this);
6537     },
6538     
6539     onClick : function(e)
6540     {
6541         if (e.getTarget('.dropdown-menu-item')) {
6542             // did you click on a menu itemm.... - then don't trigger onclick..
6543             return;
6544         }
6545         
6546         if(
6547                 this.preventDefault || 
6548                 this.href == '#' 
6549         ){
6550             Roo.log("NavItem - prevent Default?");
6551             e.preventDefault();
6552         }
6553         
6554         if (this.disabled) {
6555             return;
6556         }
6557         
6558         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6559         if (tg && tg.transition) {
6560             Roo.log("waiting for the transitionend");
6561             return;
6562         }
6563         
6564         
6565         
6566         //Roo.log("fire event clicked");
6567         if(this.fireEvent('click', this, e) === false){
6568             return;
6569         };
6570         
6571         if(this.tagtype == 'span'){
6572             return;
6573         }
6574         
6575         //Roo.log(this.href);
6576         var ael = this.el.select('a',true).first();
6577         //Roo.log(ael);
6578         
6579         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6580             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6581             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6582                 return; // ignore... - it's a 'hash' to another page.
6583             }
6584             Roo.log("NavItem - prevent Default?");
6585             e.preventDefault();
6586             this.scrollToElement(e);
6587         }
6588         
6589         
6590         var p =  this.parent();
6591    
6592         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6593             if (typeof(p.setActiveItem) !== 'undefined') {
6594                 p.setActiveItem(this);
6595             }
6596         }
6597         
6598         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6599         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6600             // remove the collapsed menu expand...
6601             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6602         }
6603     },
6604     
6605     isActive: function () {
6606         return this.active
6607     },
6608     setActive : function(state, fire, is_was_active)
6609     {
6610         if (this.active && !state && this.navId) {
6611             this.was_active = true;
6612             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6613             if (nv) {
6614                 nv.clearWasActive(this);
6615             }
6616             
6617         }
6618         this.active = state;
6619         
6620         if (!state ) {
6621             this.el.removeClass('active');
6622             this.navLink ? this.navLink.removeClass('active') : false;
6623         } else if (!this.el.hasClass('active')) {
6624             
6625             this.el.addClass('active');
6626             if (Roo.bootstrap.version == 4 && this.navLink ) {
6627                 this.navLink.addClass('active');
6628             }
6629             
6630         }
6631         if (fire) {
6632             this.fireEvent('changed', this, state);
6633         }
6634         
6635         // show a panel if it's registered and related..
6636         
6637         if (!this.navId || !this.tabId || !state || is_was_active) {
6638             return;
6639         }
6640         
6641         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6642         if (!tg) {
6643             return;
6644         }
6645         var pan = tg.getPanelByName(this.tabId);
6646         if (!pan) {
6647             return;
6648         }
6649         // if we can not flip to new panel - go back to old nav highlight..
6650         if (false == tg.showPanel(pan)) {
6651             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6652             if (nv) {
6653                 var onav = nv.getWasActive();
6654                 if (onav) {
6655                     onav.setActive(true, false, true);
6656                 }
6657             }
6658             
6659         }
6660         
6661         
6662         
6663     },
6664      // this should not be here...
6665     setDisabled : function(state)
6666     {
6667         this.disabled = state;
6668         if (!state ) {
6669             this.el.removeClass('disabled');
6670         } else if (!this.el.hasClass('disabled')) {
6671             this.el.addClass('disabled');
6672         }
6673         
6674     },
6675     
6676     /**
6677      * Fetch the element to display the tooltip on.
6678      * @return {Roo.Element} defaults to this.el
6679      */
6680     tooltipEl : function()
6681     {
6682         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6683     },
6684     
6685     scrollToElement : function(e)
6686     {
6687         var c = document.body;
6688         
6689         /*
6690          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6691          */
6692         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6693             c = document.documentElement;
6694         }
6695         
6696         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6697         
6698         if(!target){
6699             return;
6700         }
6701
6702         var o = target.calcOffsetsTo(c);
6703         
6704         var options = {
6705             target : target,
6706             value : o[1]
6707         };
6708         
6709         this.fireEvent('scrollto', this, options, e);
6710         
6711         Roo.get(c).scrollTo('top', options.value, true);
6712         
6713         return;
6714     },
6715     /**
6716      * Set the HTML (text content) of the item
6717      * @param {string} html  content for the nav item
6718      */
6719     setHtml : function(html)
6720     {
6721         this.html = html;
6722         this.htmlEl.dom.innerHTML = html;
6723         
6724     } 
6725 });
6726  
6727
6728  /*
6729  * - LGPL
6730  *
6731  * sidebar item
6732  *
6733  *  li
6734  *    <span> icon </span>
6735  *    <span> text </span>
6736  *    <span>badge </span>
6737  */
6738
6739 /**
6740  * @class Roo.bootstrap.NavSidebarItem
6741  * @extends Roo.bootstrap.NavItem
6742  * Bootstrap Navbar.NavSidebarItem class
6743  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6744  * {Boolean} open is the menu open
6745  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6746  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6747  * {String} buttonSize (sm|md|lg)the extra classes for the button
6748  * {Boolean} showArrow show arrow next to the text (default true)
6749  * @constructor
6750  * Create a new Navbar Button
6751  * @param {Object} config The config object
6752  */
6753 Roo.bootstrap.NavSidebarItem = function(config){
6754     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6755     this.addEvents({
6756         // raw events
6757         /**
6758          * @event click
6759          * The raw click event for the entire grid.
6760          * @param {Roo.EventObject} e
6761          */
6762         "click" : true,
6763          /**
6764             * @event changed
6765             * Fires when the active item active state changes
6766             * @param {Roo.bootstrap.NavSidebarItem} this
6767             * @param {boolean} state the new state
6768              
6769          */
6770         'changed': true
6771     });
6772    
6773 };
6774
6775 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6776     
6777     badgeWeight : 'default',
6778     
6779     open: false,
6780     
6781     buttonView : false,
6782     
6783     buttonWeight : 'default',
6784     
6785     buttonSize : 'md',
6786     
6787     showArrow : true,
6788     
6789     getAutoCreate : function(){
6790         
6791         
6792         var a = {
6793                 tag: 'a',
6794                 href : this.href || '#',
6795                 cls: '',
6796                 html : '',
6797                 cn : []
6798         };
6799         
6800         if(this.buttonView){
6801             a = {
6802                 tag: 'button',
6803                 href : this.href || '#',
6804                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6805                 html : this.html,
6806                 cn : []
6807             };
6808         }
6809         
6810         var cfg = {
6811             tag: 'li',
6812             cls: '',
6813             cn: [ a ]
6814         };
6815         
6816         if (this.active) {
6817             cfg.cls += ' active';
6818         }
6819         
6820         if (this.disabled) {
6821             cfg.cls += ' disabled';
6822         }
6823         if (this.open) {
6824             cfg.cls += ' open x-open';
6825         }
6826         // left icon..
6827         if (this.glyphicon || this.icon) {
6828             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6829             a.cn.push({ tag : 'i', cls : c }) ;
6830         }
6831         
6832         if(!this.buttonView){
6833             var span = {
6834                 tag: 'span',
6835                 html : this.html || ''
6836             };
6837
6838             a.cn.push(span);
6839             
6840         }
6841         
6842         if (this.badge !== '') {
6843             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6844         }
6845         
6846         if (this.menu) {
6847             
6848             if(this.showArrow){
6849                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6850             }
6851             
6852             a.cls += ' dropdown-toggle treeview' ;
6853         }
6854         
6855         return cfg;
6856     },
6857     
6858     initEvents : function()
6859     { 
6860         if (typeof (this.menu) != 'undefined') {
6861             this.menu.parentType = this.xtype;
6862             this.menu.triggerEl = this.el;
6863             this.menu = this.addxtype(Roo.apply({}, this.menu));
6864         }
6865         
6866         this.el.on('click', this.onClick, this);
6867         
6868         if(this.badge !== ''){
6869             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6870         }
6871         
6872     },
6873     
6874     onClick : function(e)
6875     {
6876         if(this.disabled){
6877             e.preventDefault();
6878             return;
6879         }
6880         
6881         if(this.preventDefault){
6882             e.preventDefault();
6883         }
6884         
6885         this.fireEvent('click', this, e);
6886     },
6887     
6888     disable : function()
6889     {
6890         this.setDisabled(true);
6891     },
6892     
6893     enable : function()
6894     {
6895         this.setDisabled(false);
6896     },
6897     
6898     setDisabled : function(state)
6899     {
6900         if(this.disabled == state){
6901             return;
6902         }
6903         
6904         this.disabled = state;
6905         
6906         if (state) {
6907             this.el.addClass('disabled');
6908             return;
6909         }
6910         
6911         this.el.removeClass('disabled');
6912         
6913         return;
6914     },
6915     
6916     setActive : function(state)
6917     {
6918         if(this.active == state){
6919             return;
6920         }
6921         
6922         this.active = state;
6923         
6924         if (state) {
6925             this.el.addClass('active');
6926             return;
6927         }
6928         
6929         this.el.removeClass('active');
6930         
6931         return;
6932     },
6933     
6934     isActive: function () 
6935     {
6936         return this.active;
6937     },
6938     
6939     setBadge : function(str)
6940     {
6941         if(!this.badgeEl){
6942             return;
6943         }
6944         
6945         this.badgeEl.dom.innerHTML = str;
6946     }
6947     
6948    
6949      
6950  
6951 });
6952  
6953
6954  /*
6955  * - LGPL
6956  *
6957  *  Breadcrumb Nav
6958  * 
6959  */
6960 Roo.namespace('Roo.bootstrap.breadcrumb');
6961
6962
6963 /**
6964  * @class Roo.bootstrap.breadcrumb.Nav
6965  * @extends Roo.bootstrap.Component
6966  * Bootstrap Breadcrumb Nav Class
6967  *  
6968  * @children Roo.bootstrap.breadcrumb.Item
6969  * 
6970  * @constructor
6971  * Create a new breadcrumb.Nav
6972  * @param {Object} config The config object
6973  */
6974
6975
6976 Roo.bootstrap.breadcrumb.Nav = function(config){
6977     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6978     
6979     
6980 };
6981
6982 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6983     
6984     getAutoCreate : function()
6985     {
6986
6987         var cfg = {
6988             tag: 'nav',
6989             cn : [
6990                 {
6991                     tag : 'ol',
6992                     cls : 'breadcrumb'
6993                 }
6994             ]
6995             
6996         };
6997           
6998         return cfg;
6999     },
7000     
7001     initEvents: function()
7002     {
7003         this.olEl = this.el.select('ol',true).first();    
7004     },
7005     getChildContainer : function()
7006     {
7007         return this.olEl;  
7008     }
7009     
7010 });
7011
7012  /*
7013  * - LGPL
7014  *
7015  *  Breadcrumb Item
7016  * 
7017  */
7018
7019
7020 /**
7021  * @class Roo.bootstrap.breadcrumb.Nav
7022  * @extends Roo.bootstrap.Component
7023  * Bootstrap Breadcrumb Nav Class
7024  *  
7025  * @children Roo.bootstrap.breadcrumb.Component
7026  * @cfg {String} html the content of the link.
7027  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7028  * @cfg {Boolean} active is it active
7029
7030  * 
7031  * @constructor
7032  * Create a new breadcrumb.Nav
7033  * @param {Object} config The config object
7034  */
7035
7036 Roo.bootstrap.breadcrumb.Item = function(config){
7037     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7038     this.addEvents({
7039         // img events
7040         /**
7041          * @event click
7042          * The img click event for the img.
7043          * @param {Roo.EventObject} e
7044          */
7045         "click" : true
7046     });
7047     
7048 };
7049
7050 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7051     
7052     href: false,
7053     html : '',
7054     
7055     getAutoCreate : function()
7056     {
7057
7058         var cfg = {
7059             tag: 'li',
7060             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7061         };
7062         if (this.href !== false) {
7063             cfg.cn = [{
7064                 tag : 'a',
7065                 href : this.href,
7066                 html : this.html
7067             }];
7068         } else {
7069             cfg.html = this.html;
7070         }
7071         
7072         return cfg;
7073     },
7074     
7075     initEvents: function()
7076     {
7077         if (this.href) {
7078             this.el.select('a', true).first().on('click',this.onClick, this)
7079         }
7080         
7081     },
7082     onClick : function(e)
7083     {
7084         e.preventDefault();
7085         this.fireEvent('click',this,  e);
7086     }
7087     
7088 });
7089
7090  /*
7091  * - LGPL
7092  *
7093  * row
7094  * 
7095  */
7096
7097 /**
7098  * @class Roo.bootstrap.Row
7099  * @extends Roo.bootstrap.Component
7100  * Bootstrap Row class (contains columns...)
7101  * 
7102  * @constructor
7103  * Create a new Row
7104  * @param {Object} config The config object
7105  */
7106
7107 Roo.bootstrap.Row = function(config){
7108     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7109 };
7110
7111 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7112     
7113     getAutoCreate : function(){
7114        return {
7115             cls: 'row clearfix'
7116        };
7117     }
7118     
7119     
7120 });
7121
7122  
7123
7124  /*
7125  * - LGPL
7126  *
7127  * pagination
7128  * 
7129  */
7130
7131 /**
7132  * @class Roo.bootstrap.Pagination
7133  * @extends Roo.bootstrap.Component
7134  * Bootstrap Pagination class
7135  * @cfg {String} size xs | sm | md | lg
7136  * @cfg {Boolean} inverse false | true
7137  * 
7138  * @constructor
7139  * Create a new Pagination
7140  * @param {Object} config The config object
7141  */
7142
7143 Roo.bootstrap.Pagination = function(config){
7144     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7145 };
7146
7147 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7148     
7149     cls: false,
7150     size: false,
7151     inverse: false,
7152     
7153     getAutoCreate : function(){
7154         var cfg = {
7155             tag: 'ul',
7156                 cls: 'pagination'
7157         };
7158         if (this.inverse) {
7159             cfg.cls += ' inverse';
7160         }
7161         if (this.html) {
7162             cfg.html=this.html;
7163         }
7164         if (this.cls) {
7165             cfg.cls += " " + this.cls;
7166         }
7167         return cfg;
7168     }
7169    
7170 });
7171
7172  
7173
7174  /*
7175  * - LGPL
7176  *
7177  * Pagination item
7178  * 
7179  */
7180
7181
7182 /**
7183  * @class Roo.bootstrap.PaginationItem
7184  * @extends Roo.bootstrap.Component
7185  * Bootstrap PaginationItem class
7186  * @cfg {String} html text
7187  * @cfg {String} href the link
7188  * @cfg {Boolean} preventDefault (true | false) default true
7189  * @cfg {Boolean} active (true | false) default false
7190  * @cfg {Boolean} disabled default false
7191  * 
7192  * 
7193  * @constructor
7194  * Create a new PaginationItem
7195  * @param {Object} config The config object
7196  */
7197
7198
7199 Roo.bootstrap.PaginationItem = function(config){
7200     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7201     this.addEvents({
7202         // raw events
7203         /**
7204          * @event click
7205          * The raw click event for the entire grid.
7206          * @param {Roo.EventObject} e
7207          */
7208         "click" : true
7209     });
7210 };
7211
7212 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7213     
7214     href : false,
7215     html : false,
7216     preventDefault: true,
7217     active : false,
7218     cls : false,
7219     disabled: false,
7220     
7221     getAutoCreate : function(){
7222         var cfg= {
7223             tag: 'li',
7224             cn: [
7225                 {
7226                     tag : 'a',
7227                     href : this.href ? this.href : '#',
7228                     html : this.html ? this.html : ''
7229                 }
7230             ]
7231         };
7232         
7233         if(this.cls){
7234             cfg.cls = this.cls;
7235         }
7236         
7237         if(this.disabled){
7238             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7239         }
7240         
7241         if(this.active){
7242             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7243         }
7244         
7245         return cfg;
7246     },
7247     
7248     initEvents: function() {
7249         
7250         this.el.on('click', this.onClick, this);
7251         
7252     },
7253     onClick : function(e)
7254     {
7255         Roo.log('PaginationItem on click ');
7256         if(this.preventDefault){
7257             e.preventDefault();
7258         }
7259         
7260         if(this.disabled){
7261             return;
7262         }
7263         
7264         this.fireEvent('click', this, e);
7265     }
7266    
7267 });
7268
7269  
7270
7271  /*
7272  * - LGPL
7273  *
7274  * slider
7275  * 
7276  */
7277
7278
7279 /**
7280  * @class Roo.bootstrap.Slider
7281  * @extends Roo.bootstrap.Component
7282  * Bootstrap Slider class
7283  *    
7284  * @constructor
7285  * Create a new Slider
7286  * @param {Object} config The config object
7287  */
7288
7289 Roo.bootstrap.Slider = function(config){
7290     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7291 };
7292
7293 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7294     
7295     getAutoCreate : function(){
7296         
7297         var cfg = {
7298             tag: 'div',
7299             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7300             cn: [
7301                 {
7302                     tag: 'a',
7303                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7304                 }
7305             ]
7306         };
7307         
7308         return cfg;
7309     }
7310    
7311 });
7312
7313  /*
7314  * Based on:
7315  * Ext JS Library 1.1.1
7316  * Copyright(c) 2006-2007, Ext JS, LLC.
7317  *
7318  * Originally Released Under LGPL - original licence link has changed is not relivant.
7319  *
7320  * Fork - LGPL
7321  * <script type="text/javascript">
7322  */
7323  /**
7324  * @extends Roo.dd.DDProxy
7325  * @class Roo.grid.SplitDragZone
7326  * Support for Column Header resizing
7327  * @constructor
7328  * @param {Object} config
7329  */
7330 // private
7331 // This is a support class used internally by the Grid components
7332 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7333     this.grid = grid;
7334     this.view = grid.getView();
7335     this.proxy = this.view.resizeProxy;
7336     Roo.grid.SplitDragZone.superclass.constructor.call(
7337         this,
7338         hd, // ID
7339         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7340         {  // CONFIG
7341             dragElId : Roo.id(this.proxy.dom),
7342             resizeFrame:false
7343         }
7344     );
7345     
7346     this.setHandleElId(Roo.id(hd));
7347     if (hd2 !== false) {
7348         this.setOuterHandleElId(Roo.id(hd2));
7349     }
7350     
7351     this.scroll = false;
7352 };
7353 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7354     fly: Roo.Element.fly,
7355
7356     b4StartDrag : function(x, y){
7357         this.view.headersDisabled = true;
7358         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7359                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7360         );
7361         this.proxy.setHeight(h);
7362         
7363         // for old system colWidth really stored the actual width?
7364         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7365         // which in reality did not work.. - it worked only for fixed sizes
7366         // for resizable we need to use actual sizes.
7367         var w = this.cm.getColumnWidth(this.cellIndex);
7368         if (!this.view.mainWrap) {
7369             // bootstrap.
7370             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7371         }
7372         
7373         
7374         
7375         // this was w-this.grid.minColumnWidth;
7376         // doesnt really make sense? - w = thie curren width or the rendered one?
7377         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7378         this.resetConstraints();
7379         this.setXConstraint(minw, 1000);
7380         this.setYConstraint(0, 0);
7381         this.minX = x - minw;
7382         this.maxX = x + 1000;
7383         this.startPos = x;
7384         if (!this.view.mainWrap) { // this is Bootstrap code..
7385             this.getDragEl().style.display='block';
7386         }
7387         
7388         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7389     },
7390
7391
7392     handleMouseDown : function(e){
7393         ev = Roo.EventObject.setEvent(e);
7394         var t = this.fly(ev.getTarget());
7395         if(t.hasClass("x-grid-split")){
7396             this.cellIndex = this.view.getCellIndex(t.dom);
7397             this.split = t.dom;
7398             this.cm = this.grid.colModel;
7399             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7400                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7401             }
7402         }
7403     },
7404
7405     endDrag : function(e){
7406         this.view.headersDisabled = false;
7407         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7408         var diff = endX - this.startPos;
7409         // 
7410         var w = this.cm.getColumnWidth(this.cellIndex);
7411         if (!this.view.mainWrap) {
7412             w = 0;
7413         }
7414         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7415     },
7416
7417     autoOffset : function(){
7418         this.setDelta(0,0);
7419     }
7420 });/*
7421  * Based on:
7422  * Ext JS Library 1.1.1
7423  * Copyright(c) 2006-2007, Ext JS, LLC.
7424  *
7425  * Originally Released Under LGPL - original licence link has changed is not relivant.
7426  *
7427  * Fork - LGPL
7428  * <script type="text/javascript">
7429  */
7430
7431 /**
7432  * @class Roo.grid.AbstractSelectionModel
7433  * @extends Roo.util.Observable
7434  * @abstract
7435  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7436  * implemented by descendant classes.  This class should not be directly instantiated.
7437  * @constructor
7438  */
7439 Roo.grid.AbstractSelectionModel = function(){
7440     this.locked = false;
7441     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7442 };
7443
7444 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7445     /** @ignore Called by the grid automatically. Do not call directly. */
7446     init : function(grid){
7447         this.grid = grid;
7448         this.initEvents();
7449     },
7450
7451     /**
7452      * Locks the selections.
7453      */
7454     lock : function(){
7455         this.locked = true;
7456     },
7457
7458     /**
7459      * Unlocks the selections.
7460      */
7461     unlock : function(){
7462         this.locked = false;
7463     },
7464
7465     /**
7466      * Returns true if the selections are locked.
7467      * @return {Boolean}
7468      */
7469     isLocked : function(){
7470         return this.locked;
7471     }
7472 });/*
7473  * Based on:
7474  * Ext JS Library 1.1.1
7475  * Copyright(c) 2006-2007, Ext JS, LLC.
7476  *
7477  * Originally Released Under LGPL - original licence link has changed is not relivant.
7478  *
7479  * Fork - LGPL
7480  * <script type="text/javascript">
7481  */
7482 /**
7483  * @extends Roo.grid.AbstractSelectionModel
7484  * @class Roo.grid.RowSelectionModel
7485  * The default SelectionModel used by {@link Roo.grid.Grid}.
7486  * It supports multiple selections and keyboard selection/navigation. 
7487  * @constructor
7488  * @param {Object} config
7489  */
7490 Roo.grid.RowSelectionModel = function(config){
7491     Roo.apply(this, config);
7492     this.selections = new Roo.util.MixedCollection(false, function(o){
7493         return o.id;
7494     });
7495
7496     this.last = false;
7497     this.lastActive = false;
7498
7499     this.addEvents({
7500         /**
7501         * @event selectionchange
7502         * Fires when the selection changes
7503         * @param {SelectionModel} this
7504         */
7505        "selectionchange" : true,
7506        /**
7507         * @event afterselectionchange
7508         * Fires after the selection changes (eg. by key press or clicking)
7509         * @param {SelectionModel} this
7510         */
7511        "afterselectionchange" : true,
7512        /**
7513         * @event beforerowselect
7514         * Fires when a row is selected being selected, return false to cancel.
7515         * @param {SelectionModel} this
7516         * @param {Number} rowIndex The selected index
7517         * @param {Boolean} keepExisting False if other selections will be cleared
7518         */
7519        "beforerowselect" : true,
7520        /**
7521         * @event rowselect
7522         * Fires when a row is selected.
7523         * @param {SelectionModel} this
7524         * @param {Number} rowIndex The selected index
7525         * @param {Roo.data.Record} r The record
7526         */
7527        "rowselect" : true,
7528        /**
7529         * @event rowdeselect
7530         * Fires when a row is deselected.
7531         * @param {SelectionModel} this
7532         * @param {Number} rowIndex The selected index
7533         */
7534         "rowdeselect" : true
7535     });
7536     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7537     this.locked = false;
7538 };
7539
7540 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7541     /**
7542      * @cfg {Boolean} singleSelect
7543      * True to allow selection of only one row at a time (defaults to false)
7544      */
7545     singleSelect : false,
7546
7547     // private
7548     initEvents : function(){
7549
7550         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7551             this.grid.on("mousedown", this.handleMouseDown, this);
7552         }else{ // allow click to work like normal
7553             this.grid.on("rowclick", this.handleDragableRowClick, this);
7554         }
7555         // bootstrap does not have a view..
7556         var view = this.grid.view ? this.grid.view : this.grid;
7557         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7558             "up" : function(e){
7559                 if(!e.shiftKey){
7560                     this.selectPrevious(e.shiftKey);
7561                 }else if(this.last !== false && this.lastActive !== false){
7562                     var last = this.last;
7563                     this.selectRange(this.last,  this.lastActive-1);
7564                     view.focusRow(this.lastActive);
7565                     if(last !== false){
7566                         this.last = last;
7567                     }
7568                 }else{
7569                     this.selectFirstRow();
7570                 }
7571                 this.fireEvent("afterselectionchange", this);
7572             },
7573             "down" : function(e){
7574                 if(!e.shiftKey){
7575                     this.selectNext(e.shiftKey);
7576                 }else if(this.last !== false && this.lastActive !== false){
7577                     var last = this.last;
7578                     this.selectRange(this.last,  this.lastActive+1);
7579                     view.focusRow(this.lastActive);
7580                     if(last !== false){
7581                         this.last = last;
7582                     }
7583                 }else{
7584                     this.selectFirstRow();
7585                 }
7586                 this.fireEvent("afterselectionchange", this);
7587             },
7588             scope: this
7589         });
7590
7591          
7592         view.on("refresh", this.onRefresh, this);
7593         view.on("rowupdated", this.onRowUpdated, this);
7594         view.on("rowremoved", this.onRemove, this);
7595     },
7596
7597     // private
7598     onRefresh : function(){
7599         var ds = this.grid.ds, i, v = this.grid.view;
7600         var s = this.selections;
7601         s.each(function(r){
7602             if((i = ds.indexOfId(r.id)) != -1){
7603                 v.onRowSelect(i);
7604                 s.add(ds.getAt(i)); // updating the selection relate data
7605             }else{
7606                 s.remove(r);
7607             }
7608         });
7609     },
7610
7611     // private
7612     onRemove : function(v, index, r){
7613         this.selections.remove(r);
7614     },
7615
7616     // private
7617     onRowUpdated : function(v, index, r){
7618         if(this.isSelected(r)){
7619             v.onRowSelect(index);
7620         }
7621     },
7622
7623     /**
7624      * Select records.
7625      * @param {Array} records The records to select
7626      * @param {Boolean} keepExisting (optional) True to keep existing selections
7627      */
7628     selectRecords : function(records, keepExisting){
7629         if(!keepExisting){
7630             this.clearSelections();
7631         }
7632         var ds = this.grid.ds;
7633         for(var i = 0, len = records.length; i < len; i++){
7634             this.selectRow(ds.indexOf(records[i]), true);
7635         }
7636     },
7637
7638     /**
7639      * Gets the number of selected rows.
7640      * @return {Number}
7641      */
7642     getCount : function(){
7643         return this.selections.length;
7644     },
7645
7646     /**
7647      * Selects the first row in the grid.
7648      */
7649     selectFirstRow : function(){
7650         this.selectRow(0);
7651     },
7652
7653     /**
7654      * Select the last row.
7655      * @param {Boolean} keepExisting (optional) True to keep existing selections
7656      */
7657     selectLastRow : function(keepExisting){
7658         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7659     },
7660
7661     /**
7662      * Selects the row immediately following the last selected row.
7663      * @param {Boolean} keepExisting (optional) True to keep existing selections
7664      */
7665     selectNext : function(keepExisting){
7666         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7667             this.selectRow(this.last+1, keepExisting);
7668             var view = this.grid.view ? this.grid.view : this.grid;
7669             view.focusRow(this.last);
7670         }
7671     },
7672
7673     /**
7674      * Selects the row that precedes the last selected row.
7675      * @param {Boolean} keepExisting (optional) True to keep existing selections
7676      */
7677     selectPrevious : function(keepExisting){
7678         if(this.last){
7679             this.selectRow(this.last-1, keepExisting);
7680             var view = this.grid.view ? this.grid.view : this.grid;
7681             view.focusRow(this.last);
7682         }
7683     },
7684
7685     /**
7686      * Returns the selected records
7687      * @return {Array} Array of selected records
7688      */
7689     getSelections : function(){
7690         return [].concat(this.selections.items);
7691     },
7692
7693     /**
7694      * Returns the first selected record.
7695      * @return {Record}
7696      */
7697     getSelected : function(){
7698         return this.selections.itemAt(0);
7699     },
7700
7701
7702     /**
7703      * Clears all selections.
7704      */
7705     clearSelections : function(fast){
7706         if(this.locked) {
7707             return;
7708         }
7709         if(fast !== true){
7710             var ds = this.grid.ds;
7711             var s = this.selections;
7712             s.each(function(r){
7713                 this.deselectRow(ds.indexOfId(r.id));
7714             }, this);
7715             s.clear();
7716         }else{
7717             this.selections.clear();
7718         }
7719         this.last = false;
7720     },
7721
7722
7723     /**
7724      * Selects all rows.
7725      */
7726     selectAll : function(){
7727         if(this.locked) {
7728             return;
7729         }
7730         this.selections.clear();
7731         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7732             this.selectRow(i, true);
7733         }
7734     },
7735
7736     /**
7737      * Returns True if there is a selection.
7738      * @return {Boolean}
7739      */
7740     hasSelection : function(){
7741         return this.selections.length > 0;
7742     },
7743
7744     /**
7745      * Returns True if the specified row is selected.
7746      * @param {Number/Record} record The record or index of the record to check
7747      * @return {Boolean}
7748      */
7749     isSelected : function(index){
7750         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7751         return (r && this.selections.key(r.id) ? true : false);
7752     },
7753
7754     /**
7755      * Returns True if the specified record id is selected.
7756      * @param {String} id The id of record to check
7757      * @return {Boolean}
7758      */
7759     isIdSelected : function(id){
7760         return (this.selections.key(id) ? true : false);
7761     },
7762
7763     // private
7764     handleMouseDown : function(e, t)
7765     {
7766         var view = this.grid.view ? this.grid.view : this.grid;
7767         var rowIndex;
7768         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7769             return;
7770         };
7771         if(e.shiftKey && this.last !== false){
7772             var last = this.last;
7773             this.selectRange(last, rowIndex, e.ctrlKey);
7774             this.last = last; // reset the last
7775             view.focusRow(rowIndex);
7776         }else{
7777             var isSelected = this.isSelected(rowIndex);
7778             if(e.button !== 0 && isSelected){
7779                 view.focusRow(rowIndex);
7780             }else if(e.ctrlKey && isSelected){
7781                 this.deselectRow(rowIndex);
7782             }else if(!isSelected){
7783                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7784                 view.focusRow(rowIndex);
7785             }
7786         }
7787         this.fireEvent("afterselectionchange", this);
7788     },
7789     // private
7790     handleDragableRowClick :  function(grid, rowIndex, e) 
7791     {
7792         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7793             this.selectRow(rowIndex, false);
7794             var view = this.grid.view ? this.grid.view : this.grid;
7795             view.focusRow(rowIndex);
7796              this.fireEvent("afterselectionchange", this);
7797         }
7798     },
7799     
7800     /**
7801      * Selects multiple rows.
7802      * @param {Array} rows Array of the indexes of the row to select
7803      * @param {Boolean} keepExisting (optional) True to keep existing selections
7804      */
7805     selectRows : function(rows, keepExisting){
7806         if(!keepExisting){
7807             this.clearSelections();
7808         }
7809         for(var i = 0, len = rows.length; i < len; i++){
7810             this.selectRow(rows[i], true);
7811         }
7812     },
7813
7814     /**
7815      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7816      * @param {Number} startRow The index of the first row in the range
7817      * @param {Number} endRow The index of the last row in the range
7818      * @param {Boolean} keepExisting (optional) True to retain existing selections
7819      */
7820     selectRange : function(startRow, endRow, keepExisting){
7821         if(this.locked) {
7822             return;
7823         }
7824         if(!keepExisting){
7825             this.clearSelections();
7826         }
7827         if(startRow <= endRow){
7828             for(var i = startRow; i <= endRow; i++){
7829                 this.selectRow(i, true);
7830             }
7831         }else{
7832             for(var i = startRow; i >= endRow; i--){
7833                 this.selectRow(i, true);
7834             }
7835         }
7836     },
7837
7838     /**
7839      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7840      * @param {Number} startRow The index of the first row in the range
7841      * @param {Number} endRow The index of the last row in the range
7842      */
7843     deselectRange : function(startRow, endRow, preventViewNotify){
7844         if(this.locked) {
7845             return;
7846         }
7847         for(var i = startRow; i <= endRow; i++){
7848             this.deselectRow(i, preventViewNotify);
7849         }
7850     },
7851
7852     /**
7853      * Selects a row.
7854      * @param {Number} row The index of the row to select
7855      * @param {Boolean} keepExisting (optional) True to keep existing selections
7856      */
7857     selectRow : function(index, keepExisting, preventViewNotify){
7858         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7859             return;
7860         }
7861         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7862             if(!keepExisting || this.singleSelect){
7863                 this.clearSelections();
7864             }
7865             var r = this.grid.ds.getAt(index);
7866             this.selections.add(r);
7867             this.last = this.lastActive = index;
7868             if(!preventViewNotify){
7869                 var view = this.grid.view ? this.grid.view : this.grid;
7870                 view.onRowSelect(index);
7871             }
7872             this.fireEvent("rowselect", this, index, r);
7873             this.fireEvent("selectionchange", this);
7874         }
7875     },
7876
7877     /**
7878      * Deselects a row.
7879      * @param {Number} row The index of the row to deselect
7880      */
7881     deselectRow : function(index, preventViewNotify){
7882         if(this.locked) {
7883             return;
7884         }
7885         if(this.last == index){
7886             this.last = false;
7887         }
7888         if(this.lastActive == index){
7889             this.lastActive = false;
7890         }
7891         var r = this.grid.ds.getAt(index);
7892         this.selections.remove(r);
7893         if(!preventViewNotify){
7894             var view = this.grid.view ? this.grid.view : this.grid;
7895             view.onRowDeselect(index);
7896         }
7897         this.fireEvent("rowdeselect", this, index);
7898         this.fireEvent("selectionchange", this);
7899     },
7900
7901     // private
7902     restoreLast : function(){
7903         if(this._last){
7904             this.last = this._last;
7905         }
7906     },
7907
7908     // private
7909     acceptsNav : function(row, col, cm){
7910         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7911     },
7912
7913     // private
7914     onEditorKey : function(field, e){
7915         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7916         if(k == e.TAB){
7917             e.stopEvent();
7918             ed.completeEdit();
7919             if(e.shiftKey){
7920                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7921             }else{
7922                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7923             }
7924         }else if(k == e.ENTER && !e.ctrlKey){
7925             e.stopEvent();
7926             ed.completeEdit();
7927             if(e.shiftKey){
7928                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7929             }else{
7930                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7931             }
7932         }else if(k == e.ESC){
7933             ed.cancelEdit();
7934         }
7935         if(newCell){
7936             g.startEditing(newCell[0], newCell[1]);
7937         }
7938     }
7939 });/*
7940  * Based on:
7941  * Ext JS Library 1.1.1
7942  * Copyright(c) 2006-2007, Ext JS, LLC.
7943  *
7944  * Originally Released Under LGPL - original licence link has changed is not relivant.
7945  *
7946  * Fork - LGPL
7947  * <script type="text/javascript">
7948  */
7949  
7950
7951 /**
7952  * @class Roo.grid.ColumnModel
7953  * @extends Roo.util.Observable
7954  * This is the default implementation of a ColumnModel used by the Grid. It defines
7955  * the columns in the grid.
7956  * <br>Usage:<br>
7957  <pre><code>
7958  var colModel = new Roo.grid.ColumnModel([
7959         {header: "Ticker", width: 60, sortable: true, locked: true},
7960         {header: "Company Name", width: 150, sortable: true},
7961         {header: "Market Cap.", width: 100, sortable: true},
7962         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7963         {header: "Employees", width: 100, sortable: true, resizable: false}
7964  ]);
7965  </code></pre>
7966  * <p>
7967  
7968  * The config options listed for this class are options which may appear in each
7969  * individual column definition.
7970  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7971  * @constructor
7972  * @param {Object} config An Array of column config objects. See this class's
7973  * config objects for details.
7974 */
7975 Roo.grid.ColumnModel = function(config){
7976         /**
7977      * The config passed into the constructor
7978      */
7979     this.config = []; //config;
7980     this.lookup = {};
7981
7982     // if no id, create one
7983     // if the column does not have a dataIndex mapping,
7984     // map it to the order it is in the config
7985     for(var i = 0, len = config.length; i < len; i++){
7986         this.addColumn(config[i]);
7987         
7988     }
7989
7990     /**
7991      * The width of columns which have no width specified (defaults to 100)
7992      * @type Number
7993      */
7994     this.defaultWidth = 100;
7995
7996     /**
7997      * Default sortable of columns which have no sortable specified (defaults to false)
7998      * @type Boolean
7999      */
8000     this.defaultSortable = false;
8001
8002     this.addEvents({
8003         /**
8004              * @event widthchange
8005              * Fires when the width of a column changes.
8006              * @param {ColumnModel} this
8007              * @param {Number} columnIndex The column index
8008              * @param {Number} newWidth The new width
8009              */
8010             "widthchange": true,
8011         /**
8012              * @event headerchange
8013              * Fires when the text of a header changes.
8014              * @param {ColumnModel} this
8015              * @param {Number} columnIndex The column index
8016              * @param {Number} newText The new header text
8017              */
8018             "headerchange": true,
8019         /**
8020              * @event hiddenchange
8021              * Fires when a column is hidden or "unhidden".
8022              * @param {ColumnModel} this
8023              * @param {Number} columnIndex The column index
8024              * @param {Boolean} hidden true if hidden, false otherwise
8025              */
8026             "hiddenchange": true,
8027             /**
8028          * @event columnmoved
8029          * Fires when a column is moved.
8030          * @param {ColumnModel} this
8031          * @param {Number} oldIndex
8032          * @param {Number} newIndex
8033          */
8034         "columnmoved" : true,
8035         /**
8036          * @event columlockchange
8037          * Fires when a column's locked state is changed
8038          * @param {ColumnModel} this
8039          * @param {Number} colIndex
8040          * @param {Boolean} locked true if locked
8041          */
8042         "columnlockchange" : true
8043     });
8044     Roo.grid.ColumnModel.superclass.constructor.call(this);
8045 };
8046 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8047     /**
8048      * @cfg {String} header The header text to display in the Grid view.
8049      */
8050         /**
8051      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8052      */
8053         /**
8054      * @cfg {String} smHeader Header at Bootsrap Small width
8055      */
8056         /**
8057      * @cfg {String} mdHeader Header at Bootsrap Medium width
8058      */
8059         /**
8060      * @cfg {String} lgHeader Header at Bootsrap Large width
8061      */
8062         /**
8063      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8064      */
8065     /**
8066      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8067      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8068      * specified, the column's index is used as an index into the Record's data Array.
8069      */
8070     /**
8071      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8072      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8073      */
8074     /**
8075      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8076      * Defaults to the value of the {@link #defaultSortable} property.
8077      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8078      */
8079     /**
8080      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8081      */
8082     /**
8083      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8084      */
8085     /**
8086      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8087      */
8088     /**
8089      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8090      */
8091     /**
8092      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8093      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8094      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8095      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8096      */
8097        /**
8098      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8099      */
8100     /**
8101      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8102      */
8103     /**
8104      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8105      */
8106     /**
8107      * @cfg {String} cursor (Optional)
8108      */
8109     /**
8110      * @cfg {String} tooltip (Optional)
8111      */
8112     /**
8113      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8114      */
8115     /**
8116      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8117      */
8118     /**
8119      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8120      */
8121     /**
8122      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8123      */
8124         /**
8125      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8126      */
8127     /**
8128      * Returns the id of the column at the specified index.
8129      * @param {Number} index The column index
8130      * @return {String} the id
8131      */
8132     getColumnId : function(index){
8133         return this.config[index].id;
8134     },
8135
8136     /**
8137      * Returns the column for a specified id.
8138      * @param {String} id The column id
8139      * @return {Object} the column
8140      */
8141     getColumnById : function(id){
8142         return this.lookup[id];
8143     },
8144
8145     
8146     /**
8147      * Returns the column Object for a specified dataIndex.
8148      * @param {String} dataIndex The column dataIndex
8149      * @return {Object|Boolean} the column or false if not found
8150      */
8151     getColumnByDataIndex: function(dataIndex){
8152         var index = this.findColumnIndex(dataIndex);
8153         return index > -1 ? this.config[index] : false;
8154     },
8155     
8156     /**
8157      * Returns the index for a specified column id.
8158      * @param {String} id The column id
8159      * @return {Number} the index, or -1 if not found
8160      */
8161     getIndexById : function(id){
8162         for(var i = 0, len = this.config.length; i < len; i++){
8163             if(this.config[i].id == id){
8164                 return i;
8165             }
8166         }
8167         return -1;
8168     },
8169     
8170     /**
8171      * Returns the index for a specified column dataIndex.
8172      * @param {String} dataIndex The column dataIndex
8173      * @return {Number} the index, or -1 if not found
8174      */
8175     
8176     findColumnIndex : function(dataIndex){
8177         for(var i = 0, len = this.config.length; i < len; i++){
8178             if(this.config[i].dataIndex == dataIndex){
8179                 return i;
8180             }
8181         }
8182         return -1;
8183     },
8184     
8185     
8186     moveColumn : function(oldIndex, newIndex){
8187         var c = this.config[oldIndex];
8188         this.config.splice(oldIndex, 1);
8189         this.config.splice(newIndex, 0, c);
8190         this.dataMap = null;
8191         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8192     },
8193
8194     isLocked : function(colIndex){
8195         return this.config[colIndex].locked === true;
8196     },
8197
8198     setLocked : function(colIndex, value, suppressEvent){
8199         if(this.isLocked(colIndex) == value){
8200             return;
8201         }
8202         this.config[colIndex].locked = value;
8203         if(!suppressEvent){
8204             this.fireEvent("columnlockchange", this, colIndex, value);
8205         }
8206     },
8207
8208     getTotalLockedWidth : function(){
8209         var totalWidth = 0;
8210         for(var i = 0; i < this.config.length; i++){
8211             if(this.isLocked(i) && !this.isHidden(i)){
8212                 this.totalWidth += this.getColumnWidth(i);
8213             }
8214         }
8215         return totalWidth;
8216     },
8217
8218     getLockedCount : function(){
8219         for(var i = 0, len = this.config.length; i < len; i++){
8220             if(!this.isLocked(i)){
8221                 return i;
8222             }
8223         }
8224         
8225         return this.config.length;
8226     },
8227
8228     /**
8229      * Returns the number of columns.
8230      * @return {Number}
8231      */
8232     getColumnCount : function(visibleOnly){
8233         if(visibleOnly === true){
8234             var c = 0;
8235             for(var i = 0, len = this.config.length; i < len; i++){
8236                 if(!this.isHidden(i)){
8237                     c++;
8238                 }
8239             }
8240             return c;
8241         }
8242         return this.config.length;
8243     },
8244
8245     /**
8246      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8247      * @param {Function} fn
8248      * @param {Object} scope (optional)
8249      * @return {Array} result
8250      */
8251     getColumnsBy : function(fn, scope){
8252         var r = [];
8253         for(var i = 0, len = this.config.length; i < len; i++){
8254             var c = this.config[i];
8255             if(fn.call(scope||this, c, i) === true){
8256                 r[r.length] = c;
8257             }
8258         }
8259         return r;
8260     },
8261
8262     /**
8263      * Returns true if the specified column is sortable.
8264      * @param {Number} col The column index
8265      * @return {Boolean}
8266      */
8267     isSortable : function(col){
8268         if(typeof this.config[col].sortable == "undefined"){
8269             return this.defaultSortable;
8270         }
8271         return this.config[col].sortable;
8272     },
8273
8274     /**
8275      * Returns the rendering (formatting) function defined for the column.
8276      * @param {Number} col The column index.
8277      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8278      */
8279     getRenderer : function(col){
8280         if(!this.config[col].renderer){
8281             return Roo.grid.ColumnModel.defaultRenderer;
8282         }
8283         return this.config[col].renderer;
8284     },
8285
8286     /**
8287      * Sets the rendering (formatting) function for a column.
8288      * @param {Number} col The column index
8289      * @param {Function} fn The function to use to process the cell's raw data
8290      * to return HTML markup for the grid view. The render function is called with
8291      * the following parameters:<ul>
8292      * <li>Data value.</li>
8293      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8294      * <li>css A CSS style string to apply to the table cell.</li>
8295      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8296      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8297      * <li>Row index</li>
8298      * <li>Column index</li>
8299      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8300      */
8301     setRenderer : function(col, fn){
8302         this.config[col].renderer = fn;
8303     },
8304
8305     /**
8306      * Returns the width for the specified column.
8307      * @param {Number} col The column index
8308      * @param (optional) {String} gridSize bootstrap width size.
8309      * @return {Number}
8310      */
8311     getColumnWidth : function(col, gridSize)
8312         {
8313                 var cfg = this.config[col];
8314                 
8315                 if (typeof(gridSize) == 'undefined') {
8316                         return cfg.width * 1 || this.defaultWidth;
8317                 }
8318                 if (gridSize === false) { // if we set it..
8319                         return cfg.width || false;
8320                 }
8321                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8322                 
8323                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8324                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8325                                 continue;
8326                         }
8327                         return cfg[ sizes[i] ];
8328                 }
8329                 return 1;
8330                 
8331     },
8332
8333     /**
8334      * Sets the width for a column.
8335      * @param {Number} col The column index
8336      * @param {Number} width The new width
8337      */
8338     setColumnWidth : function(col, width, suppressEvent){
8339         this.config[col].width = width;
8340         this.totalWidth = null;
8341         if(!suppressEvent){
8342              this.fireEvent("widthchange", this, col, width);
8343         }
8344     },
8345
8346     /**
8347      * Returns the total width of all columns.
8348      * @param {Boolean} includeHidden True to include hidden column widths
8349      * @return {Number}
8350      */
8351     getTotalWidth : function(includeHidden){
8352         if(!this.totalWidth){
8353             this.totalWidth = 0;
8354             for(var i = 0, len = this.config.length; i < len; i++){
8355                 if(includeHidden || !this.isHidden(i)){
8356                     this.totalWidth += this.getColumnWidth(i);
8357                 }
8358             }
8359         }
8360         return this.totalWidth;
8361     },
8362
8363     /**
8364      * Returns the header for the specified column.
8365      * @param {Number} col The column index
8366      * @return {String}
8367      */
8368     getColumnHeader : function(col){
8369         return this.config[col].header;
8370     },
8371
8372     /**
8373      * Sets the header for a column.
8374      * @param {Number} col The column index
8375      * @param {String} header The new header
8376      */
8377     setColumnHeader : function(col, header){
8378         this.config[col].header = header;
8379         this.fireEvent("headerchange", this, col, header);
8380     },
8381
8382     /**
8383      * Returns the tooltip for the specified column.
8384      * @param {Number} col The column index
8385      * @return {String}
8386      */
8387     getColumnTooltip : function(col){
8388             return this.config[col].tooltip;
8389     },
8390     /**
8391      * Sets the tooltip for a column.
8392      * @param {Number} col The column index
8393      * @param {String} tooltip The new tooltip
8394      */
8395     setColumnTooltip : function(col, tooltip){
8396             this.config[col].tooltip = tooltip;
8397     },
8398
8399     /**
8400      * Returns the dataIndex for the specified column.
8401      * @param {Number} col The column index
8402      * @return {Number}
8403      */
8404     getDataIndex : function(col){
8405         return this.config[col].dataIndex;
8406     },
8407
8408     /**
8409      * Sets the dataIndex for a column.
8410      * @param {Number} col The column index
8411      * @param {Number} dataIndex The new dataIndex
8412      */
8413     setDataIndex : function(col, dataIndex){
8414         this.config[col].dataIndex = dataIndex;
8415     },
8416
8417     
8418     
8419     /**
8420      * Returns true if the cell is editable.
8421      * @param {Number} colIndex The column index
8422      * @param {Number} rowIndex The row index - this is nto actually used..?
8423      * @return {Boolean}
8424      */
8425     isCellEditable : function(colIndex, rowIndex){
8426         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8427     },
8428
8429     /**
8430      * Returns the editor defined for the cell/column.
8431      * return false or null to disable editing.
8432      * @param {Number} colIndex The column index
8433      * @param {Number} rowIndex The row index
8434      * @return {Object}
8435      */
8436     getCellEditor : function(colIndex, rowIndex){
8437         return this.config[colIndex].editor;
8438     },
8439
8440     /**
8441      * Sets if a column is editable.
8442      * @param {Number} col The column index
8443      * @param {Boolean} editable True if the column is editable
8444      */
8445     setEditable : function(col, editable){
8446         this.config[col].editable = editable;
8447     },
8448
8449
8450     /**
8451      * Returns true if the column is hidden.
8452      * @param {Number} colIndex The column index
8453      * @return {Boolean}
8454      */
8455     isHidden : function(colIndex){
8456         return this.config[colIndex].hidden;
8457     },
8458
8459
8460     /**
8461      * Returns true if the column width cannot be changed
8462      */
8463     isFixed : function(colIndex){
8464         return this.config[colIndex].fixed;
8465     },
8466
8467     /**
8468      * Returns true if the column can be resized
8469      * @return {Boolean}
8470      */
8471     isResizable : function(colIndex){
8472         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8473     },
8474     /**
8475      * Sets if a column is hidden.
8476      * @param {Number} colIndex The column index
8477      * @param {Boolean} hidden True if the column is hidden
8478      */
8479     setHidden : function(colIndex, hidden){
8480         this.config[colIndex].hidden = hidden;
8481         this.totalWidth = null;
8482         this.fireEvent("hiddenchange", this, colIndex, hidden);
8483     },
8484
8485     /**
8486      * Sets the editor for a column.
8487      * @param {Number} col The column index
8488      * @param {Object} editor The editor object
8489      */
8490     setEditor : function(col, editor){
8491         this.config[col].editor = editor;
8492     },
8493     /**
8494      * Add a column (experimental...) - defaults to adding to the end..
8495      * @param {Object} config 
8496     */
8497     addColumn : function(c)
8498     {
8499     
8500         var i = this.config.length;
8501         this.config[i] = c;
8502         
8503         if(typeof c.dataIndex == "undefined"){
8504             c.dataIndex = i;
8505         }
8506         if(typeof c.renderer == "string"){
8507             c.renderer = Roo.util.Format[c.renderer];
8508         }
8509         if(typeof c.id == "undefined"){
8510             c.id = Roo.id();
8511         }
8512         if(c.editor && c.editor.xtype){
8513             c.editor  = Roo.factory(c.editor, Roo.grid);
8514         }
8515         if(c.editor && c.editor.isFormField){
8516             c.editor = new Roo.grid.GridEditor(c.editor);
8517         }
8518         this.lookup[c.id] = c;
8519     }
8520     
8521 });
8522
8523 Roo.grid.ColumnModel.defaultRenderer = function(value)
8524 {
8525     if(typeof value == "object") {
8526         return value;
8527     }
8528         if(typeof value == "string" && value.length < 1){
8529             return "&#160;";
8530         }
8531     
8532         return String.format("{0}", value);
8533 };
8534
8535 // Alias for backwards compatibility
8536 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8537 /*
8538  * Based on:
8539  * Ext JS Library 1.1.1
8540  * Copyright(c) 2006-2007, Ext JS, LLC.
8541  *
8542  * Originally Released Under LGPL - original licence link has changed is not relivant.
8543  *
8544  * Fork - LGPL
8545  * <script type="text/javascript">
8546  */
8547  
8548 /**
8549  * @class Roo.LoadMask
8550  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8551  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8552  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8553  * element's UpdateManager load indicator and will be destroyed after the initial load.
8554  * @constructor
8555  * Create a new LoadMask
8556  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8557  * @param {Object} config The config object
8558  */
8559 Roo.LoadMask = function(el, config){
8560     this.el = Roo.get(el);
8561     Roo.apply(this, config);
8562     if(this.store){
8563         this.store.on('beforeload', this.onBeforeLoad, this);
8564         this.store.on('load', this.onLoad, this);
8565         this.store.on('loadexception', this.onLoadException, this);
8566         this.removeMask = false;
8567     }else{
8568         var um = this.el.getUpdateManager();
8569         um.showLoadIndicator = false; // disable the default indicator
8570         um.on('beforeupdate', this.onBeforeLoad, this);
8571         um.on('update', this.onLoad, this);
8572         um.on('failure', this.onLoad, this);
8573         this.removeMask = true;
8574     }
8575 };
8576
8577 Roo.LoadMask.prototype = {
8578     /**
8579      * @cfg {Boolean} removeMask
8580      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8581      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8582      */
8583     removeMask : false,
8584     /**
8585      * @cfg {String} msg
8586      * The text to display in a centered loading message box (defaults to 'Loading...')
8587      */
8588     msg : 'Loading...',
8589     /**
8590      * @cfg {String} msgCls
8591      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8592      */
8593     msgCls : 'x-mask-loading',
8594
8595     /**
8596      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8597      * @type Boolean
8598      */
8599     disabled: false,
8600
8601     /**
8602      * Disables the mask to prevent it from being displayed
8603      */
8604     disable : function(){
8605        this.disabled = true;
8606     },
8607
8608     /**
8609      * Enables the mask so that it can be displayed
8610      */
8611     enable : function(){
8612         this.disabled = false;
8613     },
8614     
8615     onLoadException : function()
8616     {
8617         Roo.log(arguments);
8618         
8619         if (typeof(arguments[3]) != 'undefined') {
8620             Roo.MessageBox.alert("Error loading",arguments[3]);
8621         } 
8622         /*
8623         try {
8624             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8625                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8626             }   
8627         } catch(e) {
8628             
8629         }
8630         */
8631     
8632         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8633     },
8634     // private
8635     onLoad : function()
8636     {
8637         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8638     },
8639
8640     // private
8641     onBeforeLoad : function(){
8642         if(!this.disabled){
8643             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8644         }
8645     },
8646
8647     // private
8648     destroy : function(){
8649         if(this.store){
8650             this.store.un('beforeload', this.onBeforeLoad, this);
8651             this.store.un('load', this.onLoad, this);
8652             this.store.un('loadexception', this.onLoadException, this);
8653         }else{
8654             var um = this.el.getUpdateManager();
8655             um.un('beforeupdate', this.onBeforeLoad, this);
8656             um.un('update', this.onLoad, this);
8657             um.un('failure', this.onLoad, this);
8658         }
8659     }
8660 };/**
8661  * @class Roo.bootstrap.Table
8662  * @licence LGBL
8663  * @extends Roo.bootstrap.Component
8664  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8665  * Similar to Roo.grid.Grid
8666  * <pre><code>
8667  var table = Roo.factory({
8668     xtype : 'Table',
8669     xns : Roo.bootstrap,
8670     autoSizeColumns: true,
8671     
8672     
8673     store : {
8674         xtype : 'Store',
8675         xns : Roo.data,
8676         remoteSort : true,
8677         sortInfo : { direction : 'ASC', field: 'name' },
8678         proxy : {
8679            xtype : 'HttpProxy',
8680            xns : Roo.data,
8681            method : 'GET',
8682            url : 'https://example.com/some.data.url.json'
8683         },
8684         reader : {
8685            xtype : 'JsonReader',
8686            xns : Roo.data,
8687            fields : [ 'id', 'name', whatever' ],
8688            id : 'id',
8689            root : 'data'
8690         }
8691     },
8692     cm : [
8693         {
8694             xtype : 'ColumnModel',
8695             xns : Roo.grid,
8696             align : 'center',
8697             cursor : 'pointer',
8698             dataIndex : 'is_in_group',
8699             header : "Name",
8700             sortable : true,
8701             renderer : function(v, x , r) {  
8702             
8703                 return String.format("{0}", v)
8704             }
8705             width : 3
8706         } // more columns..
8707     ],
8708     selModel : {
8709         xtype : 'RowSelectionModel',
8710         xns : Roo.bootstrap.Table
8711         // you can add listeners to catch selection change here....
8712     }
8713      
8714
8715  });
8716  // set any options
8717  grid.render(Roo.get("some-div"));
8718 </code></pre>
8719
8720 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8721
8722
8723
8724  *
8725  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8726  * @cfg {Roo.data.Store} store The data store to use
8727  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8728  * 
8729  * @cfg {String} cls table class
8730  *
8731  * 
8732  * @cfg {boolean} striped Should the rows be alternative striped
8733  * @cfg {boolean} bordered Add borders to the table
8734  * @cfg {boolean} hover Add hover highlighting
8735  * @cfg {boolean} condensed Format condensed
8736  * @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,
8737  *                also adds table-responsive (see bootstrap docs for details)
8738  * @cfg {Boolean} loadMask (true|false) default false
8739  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8740  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8741  * @cfg {Boolean} rowSelection (true|false) default false
8742  * @cfg {Boolean} cellSelection (true|false) default false
8743  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8744  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8745  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8746  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8747  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8748  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8749  * 
8750  * @constructor
8751  * Create a new Table
8752  * @param {Object} config The config object
8753  */
8754
8755 Roo.bootstrap.Table = function(config)
8756 {
8757     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8758      
8759     // BC...
8760     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8761     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8762     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8763     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8764     
8765     this.view = this; // compat with grid.
8766     
8767     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8768     if (this.sm) {
8769         this.sm.grid = this;
8770         this.selModel = Roo.factory(this.sm, Roo.grid);
8771         this.sm = this.selModel;
8772         this.sm.xmodule = this.xmodule || false;
8773     }
8774     
8775     if (this.cm && typeof(this.cm.config) == 'undefined') {
8776         this.colModel = new Roo.grid.ColumnModel(this.cm);
8777         this.cm = this.colModel;
8778         this.cm.xmodule = this.xmodule || false;
8779     }
8780     if (this.store) {
8781         this.store= Roo.factory(this.store, Roo.data);
8782         this.ds = this.store;
8783         this.ds.xmodule = this.xmodule || false;
8784          
8785     }
8786     if (this.footer && this.store) {
8787         this.footer.dataSource = this.ds;
8788         this.footer = Roo.factory(this.footer);
8789     }
8790     
8791     /** @private */
8792     this.addEvents({
8793         /**
8794          * @event cellclick
8795          * Fires when a cell is clicked
8796          * @param {Roo.bootstrap.Table} this
8797          * @param {Roo.Element} el
8798          * @param {Number} rowIndex
8799          * @param {Number} columnIndex
8800          * @param {Roo.EventObject} e
8801          */
8802         "cellclick" : true,
8803         /**
8804          * @event celldblclick
8805          * Fires when a cell is double clicked
8806          * @param {Roo.bootstrap.Table} this
8807          * @param {Roo.Element} el
8808          * @param {Number} rowIndex
8809          * @param {Number} columnIndex
8810          * @param {Roo.EventObject} e
8811          */
8812         "celldblclick" : true,
8813         /**
8814          * @event rowclick
8815          * Fires when a row is clicked
8816          * @param {Roo.bootstrap.Table} this
8817          * @param {Roo.Element} el
8818          * @param {Number} rowIndex
8819          * @param {Roo.EventObject} e
8820          */
8821         "rowclick" : true,
8822         /**
8823          * @event rowdblclick
8824          * Fires when a row is double clicked
8825          * @param {Roo.bootstrap.Table} this
8826          * @param {Roo.Element} el
8827          * @param {Number} rowIndex
8828          * @param {Roo.EventObject} e
8829          */
8830         "rowdblclick" : true,
8831         /**
8832          * @event mouseover
8833          * Fires when a mouseover occur
8834          * @param {Roo.bootstrap.Table} this
8835          * @param {Roo.Element} el
8836          * @param {Number} rowIndex
8837          * @param {Number} columnIndex
8838          * @param {Roo.EventObject} e
8839          */
8840         "mouseover" : true,
8841         /**
8842          * @event mouseout
8843          * Fires when a mouseout occur
8844          * @param {Roo.bootstrap.Table} this
8845          * @param {Roo.Element} el
8846          * @param {Number} rowIndex
8847          * @param {Number} columnIndex
8848          * @param {Roo.EventObject} e
8849          */
8850         "mouseout" : true,
8851         /**
8852          * @event rowclass
8853          * Fires when a row is rendered, so you can change add a style to it.
8854          * @param {Roo.bootstrap.Table} this
8855          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8856          */
8857         'rowclass' : true,
8858           /**
8859          * @event rowsrendered
8860          * Fires when all the  rows have been rendered
8861          * @param {Roo.bootstrap.Table} this
8862          */
8863         'rowsrendered' : true,
8864         /**
8865          * @event contextmenu
8866          * The raw contextmenu event for the entire grid.
8867          * @param {Roo.EventObject} e
8868          */
8869         "contextmenu" : true,
8870         /**
8871          * @event rowcontextmenu
8872          * Fires when a row is right clicked
8873          * @param {Roo.bootstrap.Table} this
8874          * @param {Number} rowIndex
8875          * @param {Roo.EventObject} e
8876          */
8877         "rowcontextmenu" : true,
8878         /**
8879          * @event cellcontextmenu
8880          * Fires when a cell is right clicked
8881          * @param {Roo.bootstrap.Table} this
8882          * @param {Number} rowIndex
8883          * @param {Number} cellIndex
8884          * @param {Roo.EventObject} e
8885          */
8886          "cellcontextmenu" : true,
8887          /**
8888          * @event headercontextmenu
8889          * Fires when a header is right clicked
8890          * @param {Roo.bootstrap.Table} this
8891          * @param {Number} columnIndex
8892          * @param {Roo.EventObject} e
8893          */
8894         "headercontextmenu" : true,
8895         /**
8896          * @event mousedown
8897          * The raw mousedown event for the entire grid.
8898          * @param {Roo.EventObject} e
8899          */
8900         "mousedown" : true
8901         
8902     });
8903 };
8904
8905 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8906     
8907     cls: false,
8908     
8909     striped : false,
8910     scrollBody : false,
8911     bordered: false,
8912     hover:  false,
8913     condensed : false,
8914     responsive : false,
8915     sm : false,
8916     cm : false,
8917     store : false,
8918     loadMask : false,
8919     footerShow : true,
8920     headerShow : true,
8921     enableColumnResize: true,
8922   
8923     rowSelection : false,
8924     cellSelection : false,
8925     layout : false,
8926
8927     minColumnWidth : 50,
8928     
8929     // Roo.Element - the tbody
8930     bodyEl: false,  // <tbody> Roo.Element - thead element    
8931     headEl: false,  // <thead> Roo.Element - thead element
8932     resizeProxy : false, // proxy element for dragging?
8933
8934
8935     
8936     container: false, // used by gridpanel...
8937     
8938     lazyLoad : false,
8939     
8940     CSS : Roo.util.CSS,
8941     
8942     auto_hide_footer : false,
8943     
8944     view: false, // actually points to this..
8945     
8946     getAutoCreate : function()
8947     {
8948         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'table',
8952             cls : 'table', 
8953             cn : []
8954         };
8955         // this get's auto added by panel.Grid
8956         if (this.scrollBody) {
8957             cfg.cls += ' table-body-fixed';
8958         }    
8959         if (this.striped) {
8960             cfg.cls += ' table-striped';
8961         }
8962         
8963         if (this.hover) {
8964             cfg.cls += ' table-hover';
8965         }
8966         if (this.bordered) {
8967             cfg.cls += ' table-bordered';
8968         }
8969         if (this.condensed) {
8970             cfg.cls += ' table-condensed';
8971         }
8972         
8973         if (this.responsive) {
8974             cfg.cls += ' table-responsive';
8975         }
8976         
8977         if (this.cls) {
8978             cfg.cls+=  ' ' +this.cls;
8979         }
8980         
8981         
8982         
8983         if (this.layout) {
8984             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8985         }
8986         
8987         if(this.store || this.cm){
8988             if(this.headerShow){
8989                 cfg.cn.push(this.renderHeader());
8990             }
8991             
8992             cfg.cn.push(this.renderBody());
8993             
8994             if(this.footerShow){
8995                 cfg.cn.push(this.renderFooter());
8996             }
8997             // where does this come from?
8998             //cfg.cls+=  ' TableGrid';
8999         }
9000         
9001         return { cn : [ cfg ] };
9002     },
9003     
9004     initEvents : function()
9005     {   
9006         if(!this.store || !this.cm){
9007             return;
9008         }
9009         if (this.selModel) {
9010             this.selModel.initEvents();
9011         }
9012         
9013         
9014         //Roo.log('initEvents with ds!!!!');
9015         
9016         this.bodyEl = this.el.select('tbody', true).first();
9017         this.headEl = this.el.select('thead', true).first();
9018         this.mainFoot = this.el.select('tfoot', true).first();
9019         
9020         
9021         
9022         
9023         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9024             e.on('click', this.sort, this);
9025         }, this);
9026         
9027         
9028         // why is this done????? = it breaks dialogs??
9029         //this.parent().el.setStyle('position', 'relative');
9030         
9031         
9032         if (this.footer) {
9033             this.footer.parentId = this.id;
9034             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9035             
9036             if(this.lazyLoad){
9037                 this.el.select('tfoot tr td').first().addClass('hide');
9038             }
9039         } 
9040         
9041         if(this.loadMask) {
9042             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9043         }
9044         
9045         this.store.on('load', this.onLoad, this);
9046         this.store.on('beforeload', this.onBeforeLoad, this);
9047         this.store.on('update', this.onUpdate, this);
9048         this.store.on('add', this.onAdd, this);
9049         this.store.on("clear", this.clear, this);
9050         
9051         this.el.on("contextmenu", this.onContextMenu, this);
9052         
9053         
9054         this.cm.on("headerchange", this.onHeaderChange, this);
9055         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9056
9057  //?? does bodyEl get replaced on render?
9058         this.bodyEl.on("click", this.onClick, this);
9059         this.bodyEl.on("dblclick", this.onDblClick, this);        
9060         this.bodyEl.on('scroll', this.onBodyScroll, this);
9061
9062         // guessing mainbody will work - this relays usually caught by selmodel at present.
9063         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9064   
9065   
9066         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9067         
9068   
9069         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9070             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9071         }
9072         
9073         this.initCSS();
9074     },
9075     // Compatibility with grid - we implement all the view features at present.
9076     getView : function()
9077     {
9078         return this;
9079     },
9080     
9081     initCSS : function()
9082     {
9083         
9084         
9085         var cm = this.cm, styles = [];
9086         this.CSS.removeStyleSheet(this.id + '-cssrules');
9087         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9088         // we can honour xs/sm/md/xl  as widths...
9089         // we first have to decide what widht we are currently at...
9090         var sz = Roo.getGridSize();
9091         
9092         var total = 0;
9093         var last = -1;
9094         var cols = []; // visable cols.
9095         var total_abs = 0;
9096         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9097             var w = cm.getColumnWidth(i, false);
9098             if(cm.isHidden(i)){
9099                 cols.push( { rel : false, abs : 0 });
9100                 continue;
9101             }
9102             if (w !== false) {
9103                 cols.push( { rel : false, abs : w });
9104                 total_abs += w;
9105                 last = i; // not really..
9106                 continue;
9107             }
9108             var w = cm.getColumnWidth(i, sz);
9109             if (w > 0) {
9110                 last = i
9111             }
9112             total += w;
9113             cols.push( { rel : w, abs : false });
9114         }
9115         
9116         var avail = this.bodyEl.dom.clientWidth - total_abs;
9117         
9118         var unitWidth = Math.floor(avail / total);
9119         var rem = avail - (unitWidth * total);
9120         
9121         var hidden, width, pos = 0 , splithide , left;
9122         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9123             
9124             hidden = 'display:none;';
9125             left = '';
9126             width  = 'width:0px;';
9127             splithide = '';
9128             if(!cm.isHidden(i)){
9129                 hidden = '';
9130                 
9131                 
9132                 // we can honour xs/sm/md/xl ?
9133                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9134                 if (w===0) {
9135                     hidden = 'display:none;';
9136                 }
9137                 // width should return a small number...
9138                 if (i == last) {
9139                     w+=rem; // add the remaining with..
9140                 }
9141                 pos += w;
9142                 left = "left:" + (pos -4) + "px;";
9143                 width = "width:" + w+ "px;";
9144                 
9145             }
9146             if (this.responsive) {
9147                 width = '';
9148                 left = '';
9149                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9150                 splithide = 'display: none;';
9151             }
9152             
9153             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9154             if (this.headEl) {
9155                 if (i == last) {
9156                     splithide = 'display:none;';
9157                 }
9158                 
9159                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9160                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9161                 );
9162             }
9163             
9164         }
9165         //Roo.log(styles.join(''));
9166         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9167         
9168     },
9169     
9170     
9171     
9172     onContextMenu : function(e, t)
9173     {
9174         this.processEvent("contextmenu", e);
9175     },
9176     
9177     processEvent : function(name, e)
9178     {
9179         if (name != 'touchstart' ) {
9180             this.fireEvent(name, e);    
9181         }
9182         
9183         var t = e.getTarget();
9184         
9185         var cell = Roo.get(t);
9186         
9187         if(!cell){
9188             return;
9189         }
9190         
9191         if(cell.findParent('tfoot', false, true)){
9192             return;
9193         }
9194         
9195         if(cell.findParent('thead', false, true)){
9196             
9197             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9198                 cell = Roo.get(t).findParent('th', false, true);
9199                 if (!cell) {
9200                     Roo.log("failed to find th in thead?");
9201                     Roo.log(e.getTarget());
9202                     return;
9203                 }
9204             }
9205             
9206             var cellIndex = cell.dom.cellIndex;
9207             
9208             var ename = name == 'touchstart' ? 'click' : name;
9209             this.fireEvent("header" + ename, this, cellIndex, e);
9210             
9211             return;
9212         }
9213         
9214         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9215             cell = Roo.get(t).findParent('td', false, true);
9216             if (!cell) {
9217                 Roo.log("failed to find th in tbody?");
9218                 Roo.log(e.getTarget());
9219                 return;
9220             }
9221         }
9222         
9223         var row = cell.findParent('tr', false, true);
9224         var cellIndex = cell.dom.cellIndex;
9225         var rowIndex = row.dom.rowIndex - 1;
9226         
9227         if(row !== false){
9228             
9229             this.fireEvent("row" + name, this, rowIndex, e);
9230             
9231             if(cell !== false){
9232             
9233                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9234             }
9235         }
9236         
9237     },
9238     
9239     onMouseover : function(e, el)
9240     {
9241         var cell = Roo.get(el);
9242         
9243         if(!cell){
9244             return;
9245         }
9246         
9247         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9248             cell = cell.findParent('td', false, true);
9249         }
9250         
9251         var row = cell.findParent('tr', false, true);
9252         var cellIndex = cell.dom.cellIndex;
9253         var rowIndex = row.dom.rowIndex - 1; // start from 0
9254         
9255         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9256         
9257     },
9258     
9259     onMouseout : function(e, el)
9260     {
9261         var cell = Roo.get(el);
9262         
9263         if(!cell){
9264             return;
9265         }
9266         
9267         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9268             cell = cell.findParent('td', false, true);
9269         }
9270         
9271         var row = cell.findParent('tr', false, true);
9272         var cellIndex = cell.dom.cellIndex;
9273         var rowIndex = row.dom.rowIndex - 1; // start from 0
9274         
9275         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9276         
9277     },
9278     
9279     onClick : function(e, el)
9280     {
9281         var cell = Roo.get(el);
9282         
9283         if(!cell || (!this.cellSelection && !this.rowSelection)){
9284             return;
9285         }
9286         
9287         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9288             cell = cell.findParent('td', false, true);
9289         }
9290         
9291         if(!cell || typeof(cell) == 'undefined'){
9292             return;
9293         }
9294         
9295         var row = cell.findParent('tr', false, true);
9296         
9297         if(!row || typeof(row) == 'undefined'){
9298             return;
9299         }
9300         
9301         var cellIndex = cell.dom.cellIndex;
9302         var rowIndex = this.getRowIndex(row);
9303         
9304         // why??? - should these not be based on SelectionModel?
9305         //if(this.cellSelection){
9306             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9307         //}
9308         
9309         //if(this.rowSelection){
9310             this.fireEvent('rowclick', this, row, rowIndex, e);
9311         //}
9312          
9313     },
9314         
9315     onDblClick : function(e,el)
9316     {
9317         var cell = Roo.get(el);
9318         
9319         if(!cell || (!this.cellSelection && !this.rowSelection)){
9320             return;
9321         }
9322         
9323         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9324             cell = cell.findParent('td', false, true);
9325         }
9326         
9327         if(!cell || typeof(cell) == 'undefined'){
9328             return;
9329         }
9330         
9331         var row = cell.findParent('tr', false, true);
9332         
9333         if(!row || typeof(row) == 'undefined'){
9334             return;
9335         }
9336         
9337         var cellIndex = cell.dom.cellIndex;
9338         var rowIndex = this.getRowIndex(row);
9339         
9340         if(this.cellSelection){
9341             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9342         }
9343         
9344         if(this.rowSelection){
9345             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9346         }
9347     },
9348     findRowIndex : function(el)
9349     {
9350         var cell = Roo.get(el);
9351         if(!cell) {
9352             return false;
9353         }
9354         var row = cell.findParent('tr', false, true);
9355         
9356         if(!row || typeof(row) == 'undefined'){
9357             return false;
9358         }
9359         return this.getRowIndex(row);
9360     },
9361     sort : function(e,el)
9362     {
9363         var col = Roo.get(el);
9364         
9365         if(!col.hasClass('sortable')){
9366             return;
9367         }
9368         
9369         var sort = col.attr('sort');
9370         var dir = 'ASC';
9371         
9372         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9373             dir = 'DESC';
9374         }
9375         
9376         this.store.sortInfo = {field : sort, direction : dir};
9377         
9378         if (this.footer) {
9379             Roo.log("calling footer first");
9380             this.footer.onClick('first');
9381         } else {
9382         
9383             this.store.load({ params : { start : 0 } });
9384         }
9385     },
9386     
9387     renderHeader : function()
9388     {
9389         var header = {
9390             tag: 'thead',
9391             cn : []
9392         };
9393         
9394         var cm = this.cm;
9395         this.totalWidth = 0;
9396         
9397         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9398             
9399             var config = cm.config[i];
9400             
9401             var c = {
9402                 tag: 'th',
9403                 cls : 'x-hcol-' + i,
9404                 style : '',
9405                 
9406                 html: cm.getColumnHeader(i)
9407             };
9408             
9409             var tooltip = cm.getColumnTooltip(i);
9410             if (tooltip) {
9411                 c.tooltip = tooltip;
9412             }
9413             
9414             
9415             var hh = '';
9416             
9417             if(typeof(config.sortable) != 'undefined' && config.sortable){
9418                 c.cls += ' sortable';
9419                 c.html = '<i class="fa"></i>' + c.html;
9420             }
9421             
9422             // could use BS4 hidden-..-down 
9423             
9424             if(typeof(config.lgHeader) != 'undefined'){
9425                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9426             }
9427             
9428             if(typeof(config.mdHeader) != 'undefined'){
9429                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9430             }
9431             
9432             if(typeof(config.smHeader) != 'undefined'){
9433                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9434             }
9435             
9436             if(typeof(config.xsHeader) != 'undefined'){
9437                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9438             }
9439             
9440             if(hh.length){
9441                 c.html = hh;
9442             }
9443             
9444             if(typeof(config.tooltip) != 'undefined'){
9445                 c.tooltip = config.tooltip;
9446             }
9447             
9448             if(typeof(config.colspan) != 'undefined'){
9449                 c.colspan = config.colspan;
9450             }
9451             
9452             // hidden is handled by CSS now
9453             
9454             if(typeof(config.dataIndex) != 'undefined'){
9455                 c.sort = config.dataIndex;
9456             }
9457             
9458            
9459             
9460             if(typeof(config.align) != 'undefined' && config.align.length){
9461                 c.style += ' text-align:' + config.align + ';';
9462             }
9463             
9464             /* width is done in CSS
9465              *if(typeof(config.width) != 'undefined'){
9466                 c.style += ' width:' + config.width + 'px;';
9467                 this.totalWidth += config.width;
9468             } else {
9469                 this.totalWidth += 100; // assume minimum of 100 per column?
9470             }
9471             */
9472             
9473             if(typeof(config.cls) != 'undefined'){
9474                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9475             }
9476             // this is the bit that doesnt reall work at all...
9477             
9478             if (this.responsive) {
9479                  
9480             
9481                 ['xs','sm','md','lg'].map(function(size){
9482                     
9483                     if(typeof(config[size]) == 'undefined'){
9484                         return;
9485                     }
9486                      
9487                     if (!config[size]) { // 0 = hidden
9488                         // BS 4 '0' is treated as hide that column and below.
9489                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9490                         return;
9491                     }
9492                     
9493                     c.cls += ' col-' + size + '-' + config[size] + (
9494                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9495                     );
9496                     
9497                     
9498                 });
9499             }
9500             // at the end?
9501             
9502             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9503             
9504             
9505             
9506             
9507             header.cn.push(c)
9508         }
9509         
9510         return header;
9511     },
9512     
9513     renderBody : function()
9514     {
9515         var body = {
9516             tag: 'tbody',
9517             cn : [
9518                 {
9519                     tag: 'tr',
9520                     cn : [
9521                         {
9522                             tag : 'td',
9523                             colspan :  this.cm.getColumnCount()
9524                         }
9525                     ]
9526                 }
9527             ]
9528         };
9529         
9530         return body;
9531     },
9532     
9533     renderFooter : function()
9534     {
9535         var footer = {
9536             tag: 'tfoot',
9537             cn : [
9538                 {
9539                     tag: 'tr',
9540                     cn : [
9541                         {
9542                             tag : 'td',
9543                             colspan :  this.cm.getColumnCount()
9544                         }
9545                     ]
9546                 }
9547             ]
9548         };
9549         
9550         return footer;
9551     },
9552     
9553     
9554     
9555     onLoad : function()
9556     {
9557 //        Roo.log('ds onload');
9558         this.clear();
9559         
9560         var _this = this;
9561         var cm = this.cm;
9562         var ds = this.store;
9563         
9564         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9565             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9566             if (_this.store.sortInfo) {
9567                     
9568                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9569                     e.select('i', true).addClass(['fa-arrow-up']);
9570                 }
9571                 
9572                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9573                     e.select('i', true).addClass(['fa-arrow-down']);
9574                 }
9575             }
9576         });
9577         
9578         var tbody =  this.bodyEl;
9579               
9580         if(ds.getCount() > 0){
9581             ds.data.each(function(d,rowIndex){
9582                 var row =  this.renderRow(cm, ds, rowIndex);
9583                 
9584                 tbody.createChild(row);
9585                 
9586                 var _this = this;
9587                 
9588                 if(row.cellObjects.length){
9589                     Roo.each(row.cellObjects, function(r){
9590                         _this.renderCellObject(r);
9591                     })
9592                 }
9593                 
9594             }, this);
9595         }
9596         
9597         var tfoot = this.el.select('tfoot', true).first();
9598         
9599         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9600             
9601             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9602             
9603             var total = this.ds.getTotalCount();
9604             
9605             if(this.footer.pageSize < total){
9606                 this.mainFoot.show();
9607             }
9608         }
9609         
9610         Roo.each(this.el.select('tbody td', true).elements, function(e){
9611             e.on('mouseover', _this.onMouseover, _this);
9612         });
9613         
9614         Roo.each(this.el.select('tbody td', true).elements, function(e){
9615             e.on('mouseout', _this.onMouseout, _this);
9616         });
9617         this.fireEvent('rowsrendered', this);
9618         
9619         this.autoSize();
9620         
9621         this.initCSS(); /// resize cols
9622
9623         
9624     },
9625     
9626     
9627     onUpdate : function(ds,record)
9628     {
9629         this.refreshRow(record);
9630         this.autoSize();
9631     },
9632     
9633     onRemove : function(ds, record, index, isUpdate){
9634         if(isUpdate !== true){
9635             this.fireEvent("beforerowremoved", this, index, record);
9636         }
9637         var bt = this.bodyEl.dom;
9638         
9639         var rows = this.el.select('tbody > tr', true).elements;
9640         
9641         if(typeof(rows[index]) != 'undefined'){
9642             bt.removeChild(rows[index].dom);
9643         }
9644         
9645 //        if(bt.rows[index]){
9646 //            bt.removeChild(bt.rows[index]);
9647 //        }
9648         
9649         if(isUpdate !== true){
9650             //this.stripeRows(index);
9651             //this.syncRowHeights(index, index);
9652             //this.layout();
9653             this.fireEvent("rowremoved", this, index, record);
9654         }
9655     },
9656     
9657     onAdd : function(ds, records, rowIndex)
9658     {
9659         //Roo.log('on Add called');
9660         // - note this does not handle multiple adding very well..
9661         var bt = this.bodyEl.dom;
9662         for (var i =0 ; i < records.length;i++) {
9663             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9664             //Roo.log(records[i]);
9665             //Roo.log(this.store.getAt(rowIndex+i));
9666             this.insertRow(this.store, rowIndex + i, false);
9667             return;
9668         }
9669         
9670     },
9671     
9672     
9673     refreshRow : function(record){
9674         var ds = this.store, index;
9675         if(typeof record == 'number'){
9676             index = record;
9677             record = ds.getAt(index);
9678         }else{
9679             index = ds.indexOf(record);
9680             if (index < 0) {
9681                 return; // should not happen - but seems to 
9682             }
9683         }
9684         this.insertRow(ds, index, true);
9685         this.autoSize();
9686         this.onRemove(ds, record, index+1, true);
9687         this.autoSize();
9688         //this.syncRowHeights(index, index);
9689         //this.layout();
9690         this.fireEvent("rowupdated", this, index, record);
9691     },
9692     // private - called by RowSelection
9693     onRowSelect : function(rowIndex){
9694         var row = this.getRowDom(rowIndex);
9695         row.addClass(['bg-info','info']);
9696     },
9697     // private - called by RowSelection
9698     onRowDeselect : function(rowIndex)
9699     {
9700         if (rowIndex < 0) {
9701             return;
9702         }
9703         var row = this.getRowDom(rowIndex);
9704         row.removeClass(['bg-info','info']);
9705     },
9706       /**
9707      * Focuses the specified row.
9708      * @param {Number} row The row index
9709      */
9710     focusRow : function(row)
9711     {
9712         //Roo.log('GridView.focusRow');
9713         var x = this.bodyEl.dom.scrollLeft;
9714         this.focusCell(row, 0, false);
9715         this.bodyEl.dom.scrollLeft = x;
9716
9717     },
9718      /**
9719      * Focuses the specified cell.
9720      * @param {Number} row The row index
9721      * @param {Number} col The column index
9722      * @param {Boolean} hscroll false to disable horizontal scrolling
9723      */
9724     focusCell : function(row, col, hscroll)
9725     {
9726         //Roo.log('GridView.focusCell');
9727         var el = this.ensureVisible(row, col, hscroll);
9728         // not sure what focusEL achives = it's a <a> pos relative 
9729         //this.focusEl.alignTo(el, "tl-tl");
9730         //if(Roo.isGecko){
9731         //    this.focusEl.focus();
9732         //}else{
9733         //    this.focusEl.focus.defer(1, this.focusEl);
9734         //}
9735     },
9736     
9737      /**
9738      * Scrolls the specified cell into view
9739      * @param {Number} row The row index
9740      * @param {Number} col The column index
9741      * @param {Boolean} hscroll false to disable horizontal scrolling
9742      */
9743     ensureVisible : function(row, col, hscroll)
9744     {
9745         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9746         //return null; //disable for testing.
9747         if(typeof row != "number"){
9748             row = row.rowIndex;
9749         }
9750         if(row < 0 && row >= this.ds.getCount()){
9751             return  null;
9752         }
9753         col = (col !== undefined ? col : 0);
9754         var cm = this.cm;
9755         while(cm.isHidden(col)){
9756             col++;
9757         }
9758
9759         var el = this.getCellDom(row, col);
9760         if(!el){
9761             return null;
9762         }
9763         var c = this.bodyEl.dom;
9764
9765         var ctop = parseInt(el.offsetTop, 10);
9766         var cleft = parseInt(el.offsetLeft, 10);
9767         var cbot = ctop + el.offsetHeight;
9768         var cright = cleft + el.offsetWidth;
9769
9770         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9771         var ch = 0; //?? header is not withing the area?
9772         var stop = parseInt(c.scrollTop, 10);
9773         var sleft = parseInt(c.scrollLeft, 10);
9774         var sbot = stop + ch;
9775         var sright = sleft + c.clientWidth;
9776         /*
9777         Roo.log('GridView.ensureVisible:' +
9778                 ' ctop:' + ctop +
9779                 ' c.clientHeight:' + c.clientHeight +
9780                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9781                 ' stop:' + stop +
9782                 ' cbot:' + cbot +
9783                 ' sbot:' + sbot +
9784                 ' ch:' + ch  
9785                 );
9786         */
9787         if(ctop < stop){
9788             c.scrollTop = ctop;
9789             //Roo.log("set scrolltop to ctop DISABLE?");
9790         }else if(cbot > sbot){
9791             //Roo.log("set scrolltop to cbot-ch");
9792             c.scrollTop = cbot-ch;
9793         }
9794
9795         if(hscroll !== false){
9796             if(cleft < sleft){
9797                 c.scrollLeft = cleft;
9798             }else if(cright > sright){
9799                 c.scrollLeft = cright-c.clientWidth;
9800             }
9801         }
9802
9803         return el;
9804     },
9805     
9806     
9807     insertRow : function(dm, rowIndex, isUpdate){
9808         
9809         if(!isUpdate){
9810             this.fireEvent("beforerowsinserted", this, rowIndex);
9811         }
9812             //var s = this.getScrollState();
9813         var row = this.renderRow(this.cm, this.store, rowIndex);
9814         // insert before rowIndex..
9815         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9816         
9817         var _this = this;
9818                 
9819         if(row.cellObjects.length){
9820             Roo.each(row.cellObjects, function(r){
9821                 _this.renderCellObject(r);
9822             })
9823         }
9824             
9825         if(!isUpdate){
9826             this.fireEvent("rowsinserted", this, rowIndex);
9827             //this.syncRowHeights(firstRow, lastRow);
9828             //this.stripeRows(firstRow);
9829             //this.layout();
9830         }
9831         
9832     },
9833     
9834     
9835     getRowDom : function(rowIndex)
9836     {
9837         var rows = this.el.select('tbody > tr', true).elements;
9838         
9839         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9840         
9841     },
9842     getCellDom : function(rowIndex, colIndex)
9843     {
9844         var row = this.getRowDom(rowIndex);
9845         if (row === false) {
9846             return false;
9847         }
9848         var cols = row.select('td', true).elements;
9849         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9850         
9851     },
9852     
9853     // returns the object tree for a tr..
9854   
9855     
9856     renderRow : function(cm, ds, rowIndex) 
9857     {
9858         var d = ds.getAt(rowIndex);
9859         
9860         var row = {
9861             tag : 'tr',
9862             cls : 'x-row-' + rowIndex,
9863             cn : []
9864         };
9865             
9866         var cellObjects = [];
9867         
9868         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9869             var config = cm.config[i];
9870             
9871             var renderer = cm.getRenderer(i);
9872             var value = '';
9873             var id = false;
9874             
9875             if(typeof(renderer) !== 'undefined'){
9876                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9877             }
9878             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9879             // and are rendered into the cells after the row is rendered - using the id for the element.
9880             
9881             if(typeof(value) === 'object'){
9882                 id = Roo.id();
9883                 cellObjects.push({
9884                     container : id,
9885                     cfg : value 
9886                 })
9887             }
9888             
9889             var rowcfg = {
9890                 record: d,
9891                 rowIndex : rowIndex,
9892                 colIndex : i,
9893                 rowClass : ''
9894             };
9895
9896             this.fireEvent('rowclass', this, rowcfg);
9897             
9898             var td = {
9899                 tag: 'td',
9900                 // this might end up displaying HTML?
9901                 // this is too messy... - better to only do it on columsn you know are going to be too long
9902                 //tooltip : (typeof(value) === 'object') ? '' : value,
9903                 cls : rowcfg.rowClass + ' x-col-' + i,
9904                 style: '',
9905                 html: (typeof(value) === 'object') ? '' : value
9906             };
9907             
9908             if (id) {
9909                 td.id = id;
9910             }
9911             
9912             if(typeof(config.colspan) != 'undefined'){
9913                 td.colspan = config.colspan;
9914             }
9915             
9916             
9917             
9918             if(typeof(config.align) != 'undefined' && config.align.length){
9919                 td.style += ' text-align:' + config.align + ';';
9920             }
9921             if(typeof(config.valign) != 'undefined' && config.valign.length){
9922                 td.style += ' vertical-align:' + config.valign + ';';
9923             }
9924             /*
9925             if(typeof(config.width) != 'undefined'){
9926                 td.style += ' width:' +  config.width + 'px;';
9927             }
9928             */
9929             
9930             if(typeof(config.cursor) != 'undefined'){
9931                 td.style += ' cursor:' +  config.cursor + ';';
9932             }
9933             
9934             if(typeof(config.cls) != 'undefined'){
9935                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9936             }
9937             if (this.responsive) {
9938                 ['xs','sm','md','lg'].map(function(size){
9939                     
9940                     if(typeof(config[size]) == 'undefined'){
9941                         return;
9942                     }
9943                     
9944                     
9945                       
9946                     if (!config[size]) { // 0 = hidden
9947                         // BS 4 '0' is treated as hide that column and below.
9948                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9949                         return;
9950                     }
9951                     
9952                     td.cls += ' col-' + size + '-' + config[size] + (
9953                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9954                     );
9955                      
9956     
9957                 });
9958             }
9959             row.cn.push(td);
9960            
9961         }
9962         
9963         row.cellObjects = cellObjects;
9964         
9965         return row;
9966           
9967     },
9968     
9969     
9970     
9971     onBeforeLoad : function()
9972     {
9973         
9974     },
9975      /**
9976      * Remove all rows
9977      */
9978     clear : function()
9979     {
9980         this.el.select('tbody', true).first().dom.innerHTML = '';
9981     },
9982     /**
9983      * Show or hide a row.
9984      * @param {Number} rowIndex to show or hide
9985      * @param {Boolean} state hide
9986      */
9987     setRowVisibility : function(rowIndex, state)
9988     {
9989         var bt = this.bodyEl.dom;
9990         
9991         var rows = this.el.select('tbody > tr', true).elements;
9992         
9993         if(typeof(rows[rowIndex]) == 'undefined'){
9994             return;
9995         }
9996         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9997         
9998     },
9999     
10000     
10001     getSelectionModel : function(){
10002         if(!this.selModel){
10003             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10004         }
10005         return this.selModel;
10006     },
10007     /*
10008      * Render the Roo.bootstrap object from renderder
10009      */
10010     renderCellObject : function(r)
10011     {
10012         var _this = this;
10013         
10014         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10015         
10016         var t = r.cfg.render(r.container);
10017         
10018         if(r.cfg.cn){
10019             Roo.each(r.cfg.cn, function(c){
10020                 var child = {
10021                     container: t.getChildContainer(),
10022                     cfg: c
10023                 };
10024                 _this.renderCellObject(child);
10025             })
10026         }
10027     },
10028     /**
10029      * get the Row Index from a dom element.
10030      * @param {Roo.Element} row The row to look for
10031      * @returns {Number} the row
10032      */
10033     getRowIndex : function(row)
10034     {
10035         var rowIndex = -1;
10036         
10037         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10038             if(el != row){
10039                 return;
10040             }
10041             
10042             rowIndex = index;
10043         });
10044         
10045         return rowIndex;
10046     },
10047     /**
10048      * get the header TH element for columnIndex
10049      * @param {Number} columnIndex
10050      * @returns {Roo.Element}
10051      */
10052     getHeaderIndex: function(colIndex)
10053     {
10054         var cols = this.headEl.select('th', true).elements;
10055         return cols[colIndex]; 
10056     },
10057     /**
10058      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10059      * @param {domElement} cell to look for
10060      * @returns {Number} the column
10061      */
10062     getCellIndex : function(cell)
10063     {
10064         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10065         if(id){
10066             return parseInt(id[1], 10);
10067         }
10068         return 0;
10069     },
10070      /**
10071      * Returns the grid's underlying element = used by panel.Grid
10072      * @return {Element} The element
10073      */
10074     getGridEl : function(){
10075         return this.el;
10076     },
10077      /**
10078      * Forces a resize - used by panel.Grid
10079      * @return {Element} The element
10080      */
10081     autoSize : function()
10082     {
10083         //var ctr = Roo.get(this.container.dom.parentElement);
10084         var ctr = Roo.get(this.el.dom);
10085         
10086         var thd = this.getGridEl().select('thead',true).first();
10087         var tbd = this.getGridEl().select('tbody', true).first();
10088         var tfd = this.getGridEl().select('tfoot', true).first();
10089         
10090         var cw = ctr.getWidth();
10091         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10092         
10093         if (tbd) {
10094             
10095             tbd.setWidth(ctr.getWidth());
10096             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10097             // this needs fixing for various usage - currently only hydra job advers I think..
10098             //tdb.setHeight(
10099             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10100             //); 
10101             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10102             cw -= barsize;
10103         }
10104         cw = Math.max(cw, this.totalWidth);
10105         this.getGridEl().select('tbody tr',true).setWidth(cw);
10106         this.initCSS();
10107         
10108         // resize 'expandable coloumn?
10109         
10110         return; // we doe not have a view in this design..
10111         
10112     },
10113     onBodyScroll: function()
10114     {
10115         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10116         if(this.headEl){
10117             this.headEl.setStyle({
10118                 'position' : 'relative',
10119                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10120             });
10121         }
10122         
10123         if(this.lazyLoad){
10124             
10125             var scrollHeight = this.bodyEl.dom.scrollHeight;
10126             
10127             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10128             
10129             var height = this.bodyEl.getHeight();
10130             
10131             if(scrollHeight - height == scrollTop) {
10132                 
10133                 var total = this.ds.getTotalCount();
10134                 
10135                 if(this.footer.cursor + this.footer.pageSize < total){
10136                     
10137                     this.footer.ds.load({
10138                         params : {
10139                             start : this.footer.cursor + this.footer.pageSize,
10140                             limit : this.footer.pageSize
10141                         },
10142                         add : true
10143                     });
10144                 }
10145             }
10146             
10147         }
10148     },
10149     onColumnSplitterMoved : function(i, diff)
10150     {
10151         this.userResized = true;
10152         
10153         var cm = this.colModel;
10154         
10155         var w = this.getHeaderIndex(i).getWidth() + diff;
10156         
10157         
10158         cm.setColumnWidth(i, w, true);
10159         this.initCSS();
10160         //var cid = cm.getColumnId(i); << not used in this version?
10161        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10162         
10163         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10164         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10165         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10166 */
10167         //this.updateSplitters();
10168         //this.layout(); << ??
10169         this.fireEvent("columnresize", i, w);
10170     },
10171     onHeaderChange : function()
10172     {
10173         var header = this.renderHeader();
10174         var table = this.el.select('table', true).first();
10175         
10176         this.headEl.remove();
10177         this.headEl = table.createChild(header, this.bodyEl, false);
10178         
10179         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10180             e.on('click', this.sort, this);
10181         }, this);
10182         
10183         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10184             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10185         }
10186         
10187     },
10188     
10189     onHiddenChange : function(colModel, colIndex, hidden)
10190     {
10191         /*
10192         this.cm.setHidden()
10193         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10194         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10195         
10196         this.CSS.updateRule(thSelector, "display", "");
10197         this.CSS.updateRule(tdSelector, "display", "");
10198         
10199         if(hidden){
10200             this.CSS.updateRule(thSelector, "display", "none");
10201             this.CSS.updateRule(tdSelector, "display", "none");
10202         }
10203         */
10204         // onload calls initCSS()
10205         this.onHeaderChange();
10206         this.onLoad();
10207     },
10208     
10209     setColumnWidth: function(col_index, width)
10210     {
10211         // width = "md-2 xs-2..."
10212         if(!this.colModel.config[col_index]) {
10213             return;
10214         }
10215         
10216         var w = width.split(" ");
10217         
10218         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10219         
10220         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10221         
10222         
10223         for(var j = 0; j < w.length; j++) {
10224             
10225             if(!w[j]) {
10226                 continue;
10227             }
10228             
10229             var size_cls = w[j].split("-");
10230             
10231             if(!Number.isInteger(size_cls[1] * 1)) {
10232                 continue;
10233             }
10234             
10235             if(!this.colModel.config[col_index][size_cls[0]]) {
10236                 continue;
10237             }
10238             
10239             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10240                 continue;
10241             }
10242             
10243             h_row[0].classList.replace(
10244                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10245                 "col-"+size_cls[0]+"-"+size_cls[1]
10246             );
10247             
10248             for(var i = 0; i < rows.length; i++) {
10249                 
10250                 var size_cls = w[j].split("-");
10251                 
10252                 if(!Number.isInteger(size_cls[1] * 1)) {
10253                     continue;
10254                 }
10255                 
10256                 if(!this.colModel.config[col_index][size_cls[0]]) {
10257                     continue;
10258                 }
10259                 
10260                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10261                     continue;
10262                 }
10263                 
10264                 rows[i].classList.replace(
10265                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10266                     "col-"+size_cls[0]+"-"+size_cls[1]
10267                 );
10268             }
10269             
10270             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10271         }
10272     }
10273 });
10274
10275 // currently only used to find the split on drag.. 
10276 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10277
10278 /**
10279  * @depricated
10280 */
10281 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10282 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10283 /*
10284  * - LGPL
10285  *
10286  * table cell
10287  * 
10288  */
10289
10290 /**
10291  * @class Roo.bootstrap.TableCell
10292  * @extends Roo.bootstrap.Component
10293  * Bootstrap TableCell class
10294  * @cfg {String} html cell contain text
10295  * @cfg {String} cls cell class
10296  * @cfg {String} tag cell tag (td|th) default td
10297  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10298  * @cfg {String} align Aligns the content in a cell
10299  * @cfg {String} axis Categorizes cells
10300  * @cfg {String} bgcolor Specifies the background color of a cell
10301  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10302  * @cfg {Number} colspan Specifies the number of columns a cell should span
10303  * @cfg {String} headers Specifies one or more header cells a cell is related to
10304  * @cfg {Number} height Sets the height of a cell
10305  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10306  * @cfg {Number} rowspan Sets the number of rows a cell should span
10307  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10308  * @cfg {String} valign Vertical aligns the content in a cell
10309  * @cfg {Number} width Specifies the width of a cell
10310  * 
10311  * @constructor
10312  * Create a new TableCell
10313  * @param {Object} config The config object
10314  */
10315
10316 Roo.bootstrap.TableCell = function(config){
10317     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10318 };
10319
10320 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10321     
10322     html: false,
10323     cls: false,
10324     tag: false,
10325     abbr: false,
10326     align: false,
10327     axis: false,
10328     bgcolor: false,
10329     charoff: false,
10330     colspan: false,
10331     headers: false,
10332     height: false,
10333     nowrap: false,
10334     rowspan: false,
10335     scope: false,
10336     valign: false,
10337     width: false,
10338     
10339     
10340     getAutoCreate : function(){
10341         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10342         
10343         cfg = {
10344             tag: 'td'
10345         };
10346         
10347         if(this.tag){
10348             cfg.tag = this.tag;
10349         }
10350         
10351         if (this.html) {
10352             cfg.html=this.html
10353         }
10354         if (this.cls) {
10355             cfg.cls=this.cls
10356         }
10357         if (this.abbr) {
10358             cfg.abbr=this.abbr
10359         }
10360         if (this.align) {
10361             cfg.align=this.align
10362         }
10363         if (this.axis) {
10364             cfg.axis=this.axis
10365         }
10366         if (this.bgcolor) {
10367             cfg.bgcolor=this.bgcolor
10368         }
10369         if (this.charoff) {
10370             cfg.charoff=this.charoff
10371         }
10372         if (this.colspan) {
10373             cfg.colspan=this.colspan
10374         }
10375         if (this.headers) {
10376             cfg.headers=this.headers
10377         }
10378         if (this.height) {
10379             cfg.height=this.height
10380         }
10381         if (this.nowrap) {
10382             cfg.nowrap=this.nowrap
10383         }
10384         if (this.rowspan) {
10385             cfg.rowspan=this.rowspan
10386         }
10387         if (this.scope) {
10388             cfg.scope=this.scope
10389         }
10390         if (this.valign) {
10391             cfg.valign=this.valign
10392         }
10393         if (this.width) {
10394             cfg.width=this.width
10395         }
10396         
10397         
10398         return cfg;
10399     }
10400    
10401 });
10402
10403  
10404
10405  /*
10406  * - LGPL
10407  *
10408  * table row
10409  * 
10410  */
10411
10412 /**
10413  * @class Roo.bootstrap.TableRow
10414  * @extends Roo.bootstrap.Component
10415  * Bootstrap TableRow class
10416  * @cfg {String} cls row class
10417  * @cfg {String} align Aligns the content in a table row
10418  * @cfg {String} bgcolor Specifies a background color for a table row
10419  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10420  * @cfg {String} valign Vertical aligns the content in a table row
10421  * 
10422  * @constructor
10423  * Create a new TableRow
10424  * @param {Object} config The config object
10425  */
10426
10427 Roo.bootstrap.TableRow = function(config){
10428     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10429 };
10430
10431 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10432     
10433     cls: false,
10434     align: false,
10435     bgcolor: false,
10436     charoff: false,
10437     valign: false,
10438     
10439     getAutoCreate : function(){
10440         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10441         
10442         cfg = {
10443             tag: 'tr'
10444         };
10445             
10446         if(this.cls){
10447             cfg.cls = this.cls;
10448         }
10449         if(this.align){
10450             cfg.align = this.align;
10451         }
10452         if(this.bgcolor){
10453             cfg.bgcolor = this.bgcolor;
10454         }
10455         if(this.charoff){
10456             cfg.charoff = this.charoff;
10457         }
10458         if(this.valign){
10459             cfg.valign = this.valign;
10460         }
10461         
10462         return cfg;
10463     }
10464    
10465 });
10466
10467  
10468
10469  /*
10470  * - LGPL
10471  *
10472  * table body
10473  * 
10474  */
10475
10476 /**
10477  * @class Roo.bootstrap.TableBody
10478  * @extends Roo.bootstrap.Component
10479  * Bootstrap TableBody class
10480  * @cfg {String} cls element class
10481  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10482  * @cfg {String} align Aligns the content inside the element
10483  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10484  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10485  * 
10486  * @constructor
10487  * Create a new TableBody
10488  * @param {Object} config The config object
10489  */
10490
10491 Roo.bootstrap.TableBody = function(config){
10492     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10493 };
10494
10495 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10496     
10497     cls: false,
10498     tag: false,
10499     align: false,
10500     charoff: false,
10501     valign: false,
10502     
10503     getAutoCreate : function(){
10504         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10505         
10506         cfg = {
10507             tag: 'tbody'
10508         };
10509             
10510         if (this.cls) {
10511             cfg.cls=this.cls
10512         }
10513         if(this.tag){
10514             cfg.tag = this.tag;
10515         }
10516         
10517         if(this.align){
10518             cfg.align = this.align;
10519         }
10520         if(this.charoff){
10521             cfg.charoff = this.charoff;
10522         }
10523         if(this.valign){
10524             cfg.valign = this.valign;
10525         }
10526         
10527         return cfg;
10528     }
10529     
10530     
10531 //    initEvents : function()
10532 //    {
10533 //        
10534 //        if(!this.store){
10535 //            return;
10536 //        }
10537 //        
10538 //        this.store = Roo.factory(this.store, Roo.data);
10539 //        this.store.on('load', this.onLoad, this);
10540 //        
10541 //        this.store.load();
10542 //        
10543 //    },
10544 //    
10545 //    onLoad: function () 
10546 //    {   
10547 //        this.fireEvent('load', this);
10548 //    }
10549 //    
10550 //   
10551 });
10552
10553  
10554
10555  /*
10556  * Based on:
10557  * Ext JS Library 1.1.1
10558  * Copyright(c) 2006-2007, Ext JS, LLC.
10559  *
10560  * Originally Released Under LGPL - original licence link has changed is not relivant.
10561  *
10562  * Fork - LGPL
10563  * <script type="text/javascript">
10564  */
10565
10566 // as we use this in bootstrap.
10567 Roo.namespace('Roo.form');
10568  /**
10569  * @class Roo.form.Action
10570  * Internal Class used to handle form actions
10571  * @constructor
10572  * @param {Roo.form.BasicForm} el The form element or its id
10573  * @param {Object} config Configuration options
10574  */
10575
10576  
10577  
10578 // define the action interface
10579 Roo.form.Action = function(form, options){
10580     this.form = form;
10581     this.options = options || {};
10582 };
10583 /**
10584  * Client Validation Failed
10585  * @const 
10586  */
10587 Roo.form.Action.CLIENT_INVALID = 'client';
10588 /**
10589  * Server Validation Failed
10590  * @const 
10591  */
10592 Roo.form.Action.SERVER_INVALID = 'server';
10593  /**
10594  * Connect to Server Failed
10595  * @const 
10596  */
10597 Roo.form.Action.CONNECT_FAILURE = 'connect';
10598 /**
10599  * Reading Data from Server Failed
10600  * @const 
10601  */
10602 Roo.form.Action.LOAD_FAILURE = 'load';
10603
10604 Roo.form.Action.prototype = {
10605     type : 'default',
10606     failureType : undefined,
10607     response : undefined,
10608     result : undefined,
10609
10610     // interface method
10611     run : function(options){
10612
10613     },
10614
10615     // interface method
10616     success : function(response){
10617
10618     },
10619
10620     // interface method
10621     handleResponse : function(response){
10622
10623     },
10624
10625     // default connection failure
10626     failure : function(response){
10627         
10628         this.response = response;
10629         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10630         this.form.afterAction(this, false);
10631     },
10632
10633     processResponse : function(response){
10634         this.response = response;
10635         if(!response.responseText){
10636             return true;
10637         }
10638         this.result = this.handleResponse(response);
10639         return this.result;
10640     },
10641
10642     // utility functions used internally
10643     getUrl : function(appendParams){
10644         var url = this.options.url || this.form.url || this.form.el.dom.action;
10645         if(appendParams){
10646             var p = this.getParams();
10647             if(p){
10648                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10649             }
10650         }
10651         return url;
10652     },
10653
10654     getMethod : function(){
10655         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10656     },
10657
10658     getParams : function(){
10659         var bp = this.form.baseParams;
10660         var p = this.options.params;
10661         if(p){
10662             if(typeof p == "object"){
10663                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10664             }else if(typeof p == 'string' && bp){
10665                 p += '&' + Roo.urlEncode(bp);
10666             }
10667         }else if(bp){
10668             p = Roo.urlEncode(bp);
10669         }
10670         return p;
10671     },
10672
10673     createCallback : function(){
10674         return {
10675             success: this.success,
10676             failure: this.failure,
10677             scope: this,
10678             timeout: (this.form.timeout*1000),
10679             upload: this.form.fileUpload ? this.success : undefined
10680         };
10681     }
10682 };
10683
10684 Roo.form.Action.Submit = function(form, options){
10685     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10686 };
10687
10688 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10689     type : 'submit',
10690
10691     haveProgress : false,
10692     uploadComplete : false,
10693     
10694     // uploadProgress indicator.
10695     uploadProgress : function()
10696     {
10697         if (!this.form.progressUrl) {
10698             return;
10699         }
10700         
10701         if (!this.haveProgress) {
10702             Roo.MessageBox.progress("Uploading", "Uploading");
10703         }
10704         if (this.uploadComplete) {
10705            Roo.MessageBox.hide();
10706            return;
10707         }
10708         
10709         this.haveProgress = true;
10710    
10711         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10712         
10713         var c = new Roo.data.Connection();
10714         c.request({
10715             url : this.form.progressUrl,
10716             params: {
10717                 id : uid
10718             },
10719             method: 'GET',
10720             success : function(req){
10721                //console.log(data);
10722                 var rdata = false;
10723                 var edata;
10724                 try  {
10725                    rdata = Roo.decode(req.responseText)
10726                 } catch (e) {
10727                     Roo.log("Invalid data from server..");
10728                     Roo.log(edata);
10729                     return;
10730                 }
10731                 if (!rdata || !rdata.success) {
10732                     Roo.log(rdata);
10733                     Roo.MessageBox.alert(Roo.encode(rdata));
10734                     return;
10735                 }
10736                 var data = rdata.data;
10737                 
10738                 if (this.uploadComplete) {
10739                    Roo.MessageBox.hide();
10740                    return;
10741                 }
10742                    
10743                 if (data){
10744                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10745                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10746                     );
10747                 }
10748                 this.uploadProgress.defer(2000,this);
10749             },
10750        
10751             failure: function(data) {
10752                 Roo.log('progress url failed ');
10753                 Roo.log(data);
10754             },
10755             scope : this
10756         });
10757            
10758     },
10759     
10760     
10761     run : function()
10762     {
10763         // run get Values on the form, so it syncs any secondary forms.
10764         this.form.getValues();
10765         
10766         var o = this.options;
10767         var method = this.getMethod();
10768         var isPost = method == 'POST';
10769         if(o.clientValidation === false || this.form.isValid()){
10770             
10771             if (this.form.progressUrl) {
10772                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10773                     (new Date() * 1) + '' + Math.random());
10774                     
10775             } 
10776             
10777             
10778             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10779                 form:this.form.el.dom,
10780                 url:this.getUrl(!isPost),
10781                 method: method,
10782                 params:isPost ? this.getParams() : null,
10783                 isUpload: this.form.fileUpload,
10784                 formData : this.form.formData
10785             }));
10786             
10787             this.uploadProgress();
10788
10789         }else if (o.clientValidation !== false){ // client validation failed
10790             this.failureType = Roo.form.Action.CLIENT_INVALID;
10791             this.form.afterAction(this, false);
10792         }
10793     },
10794
10795     success : function(response)
10796     {
10797         this.uploadComplete= true;
10798         if (this.haveProgress) {
10799             Roo.MessageBox.hide();
10800         }
10801         
10802         
10803         var result = this.processResponse(response);
10804         if(result === true || result.success){
10805             this.form.afterAction(this, true);
10806             return;
10807         }
10808         if(result.errors){
10809             this.form.markInvalid(result.errors);
10810             this.failureType = Roo.form.Action.SERVER_INVALID;
10811         }
10812         this.form.afterAction(this, false);
10813     },
10814     failure : function(response)
10815     {
10816         this.uploadComplete= true;
10817         if (this.haveProgress) {
10818             Roo.MessageBox.hide();
10819         }
10820         
10821         this.response = response;
10822         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10823         this.form.afterAction(this, false);
10824     },
10825     
10826     handleResponse : function(response){
10827         if(this.form.errorReader){
10828             var rs = this.form.errorReader.read(response);
10829             var errors = [];
10830             if(rs.records){
10831                 for(var i = 0, len = rs.records.length; i < len; i++) {
10832                     var r = rs.records[i];
10833                     errors[i] = r.data;
10834                 }
10835             }
10836             if(errors.length < 1){
10837                 errors = null;
10838             }
10839             return {
10840                 success : rs.success,
10841                 errors : errors
10842             };
10843         }
10844         var ret = false;
10845         try {
10846             ret = Roo.decode(response.responseText);
10847         } catch (e) {
10848             ret = {
10849                 success: false,
10850                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10851                 errors : []
10852             };
10853         }
10854         return ret;
10855         
10856     }
10857 });
10858
10859
10860 Roo.form.Action.Load = function(form, options){
10861     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10862     this.reader = this.form.reader;
10863 };
10864
10865 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10866     type : 'load',
10867
10868     run : function(){
10869         
10870         Roo.Ajax.request(Roo.apply(
10871                 this.createCallback(), {
10872                     method:this.getMethod(),
10873                     url:this.getUrl(false),
10874                     params:this.getParams()
10875         }));
10876     },
10877
10878     success : function(response){
10879         
10880         var result = this.processResponse(response);
10881         if(result === true || !result.success || !result.data){
10882             this.failureType = Roo.form.Action.LOAD_FAILURE;
10883             this.form.afterAction(this, false);
10884             return;
10885         }
10886         this.form.clearInvalid();
10887         this.form.setValues(result.data);
10888         this.form.afterAction(this, true);
10889     },
10890
10891     handleResponse : function(response){
10892         if(this.form.reader){
10893             var rs = this.form.reader.read(response);
10894             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10895             return {
10896                 success : rs.success,
10897                 data : data
10898             };
10899         }
10900         return Roo.decode(response.responseText);
10901     }
10902 });
10903
10904 Roo.form.Action.ACTION_TYPES = {
10905     'load' : Roo.form.Action.Load,
10906     'submit' : Roo.form.Action.Submit
10907 };/*
10908  * - LGPL
10909  *
10910  * form
10911  *
10912  */
10913
10914 /**
10915  * @class Roo.bootstrap.Form
10916  * @extends Roo.bootstrap.Component
10917  * Bootstrap Form class
10918  * @cfg {String} method  GET | POST (default POST)
10919  * @cfg {String} labelAlign top | left (default top)
10920  * @cfg {String} align left  | right - for navbars
10921  * @cfg {Boolean} loadMask load mask when submit (default true)
10922
10923  *
10924  * @constructor
10925  * Create a new Form
10926  * @param {Object} config The config object
10927  */
10928
10929
10930 Roo.bootstrap.Form = function(config){
10931     
10932     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10933     
10934     Roo.bootstrap.Form.popover.apply();
10935     
10936     this.addEvents({
10937         /**
10938          * @event clientvalidation
10939          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10940          * @param {Form} this
10941          * @param {Boolean} valid true if the form has passed client-side validation
10942          */
10943         clientvalidation: true,
10944         /**
10945          * @event beforeaction
10946          * Fires before any action is performed. Return false to cancel the action.
10947          * @param {Form} this
10948          * @param {Action} action The action to be performed
10949          */
10950         beforeaction: true,
10951         /**
10952          * @event actionfailed
10953          * Fires when an action fails.
10954          * @param {Form} this
10955          * @param {Action} action The action that failed
10956          */
10957         actionfailed : true,
10958         /**
10959          * @event actioncomplete
10960          * Fires when an action is completed.
10961          * @param {Form} this
10962          * @param {Action} action The action that completed
10963          */
10964         actioncomplete : true
10965     });
10966 };
10967
10968 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10969
10970      /**
10971      * @cfg {String} method
10972      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10973      */
10974     method : 'POST',
10975     /**
10976      * @cfg {String} url
10977      * The URL to use for form actions if one isn't supplied in the action options.
10978      */
10979     /**
10980      * @cfg {Boolean} fileUpload
10981      * Set to true if this form is a file upload.
10982      */
10983
10984     /**
10985      * @cfg {Object} baseParams
10986      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10987      */
10988
10989     /**
10990      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10991      */
10992     timeout: 30,
10993     /**
10994      * @cfg {Sting} align (left|right) for navbar forms
10995      */
10996     align : 'left',
10997
10998     // private
10999     activeAction : null,
11000
11001     /**
11002      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11003      * element by passing it or its id or mask the form itself by passing in true.
11004      * @type Mixed
11005      */
11006     waitMsgTarget : false,
11007
11008     loadMask : true,
11009     
11010     /**
11011      * @cfg {Boolean} errorMask (true|false) default false
11012      */
11013     errorMask : false,
11014     
11015     /**
11016      * @cfg {Number} maskOffset Default 100
11017      */
11018     maskOffset : 100,
11019     
11020     /**
11021      * @cfg {Boolean} maskBody
11022      */
11023     maskBody : false,
11024
11025     getAutoCreate : function(){
11026
11027         var cfg = {
11028             tag: 'form',
11029             method : this.method || 'POST',
11030             id : this.id || Roo.id(),
11031             cls : ''
11032         };
11033         if (this.parent().xtype.match(/^Nav/)) {
11034             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11035
11036         }
11037
11038         if (this.labelAlign == 'left' ) {
11039             cfg.cls += ' form-horizontal';
11040         }
11041
11042
11043         return cfg;
11044     },
11045     initEvents : function()
11046     {
11047         this.el.on('submit', this.onSubmit, this);
11048         // this was added as random key presses on the form where triggering form submit.
11049         this.el.on('keypress', function(e) {
11050             if (e.getCharCode() != 13) {
11051                 return true;
11052             }
11053             // we might need to allow it for textareas.. and some other items.
11054             // check e.getTarget().
11055
11056             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11057                 return true;
11058             }
11059
11060             Roo.log("keypress blocked");
11061
11062             e.preventDefault();
11063             return false;
11064         });
11065         
11066     },
11067     // private
11068     onSubmit : function(e){
11069         e.stopEvent();
11070     },
11071
11072      /**
11073      * Returns true if client-side validation on the form is successful.
11074      * @return Boolean
11075      */
11076     isValid : function(){
11077         var items = this.getItems();
11078         var valid = true;
11079         var target = false;
11080         
11081         items.each(function(f){
11082             
11083             if(f.validate()){
11084                 return;
11085             }
11086             
11087             Roo.log('invalid field: ' + f.name);
11088             
11089             valid = false;
11090
11091             if(!target && f.el.isVisible(true)){
11092                 target = f;
11093             }
11094            
11095         });
11096         
11097         if(this.errorMask && !valid){
11098             Roo.bootstrap.Form.popover.mask(this, target);
11099         }
11100         
11101         return valid;
11102     },
11103     
11104     /**
11105      * Returns true if any fields in this form have changed since their original load.
11106      * @return Boolean
11107      */
11108     isDirty : function(){
11109         var dirty = false;
11110         var items = this.getItems();
11111         items.each(function(f){
11112            if(f.isDirty()){
11113                dirty = true;
11114                return false;
11115            }
11116            return true;
11117         });
11118         return dirty;
11119     },
11120      /**
11121      * Performs a predefined action (submit or load) or custom actions you define on this form.
11122      * @param {String} actionName The name of the action type
11123      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11124      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11125      * accept other config options):
11126      * <pre>
11127 Property          Type             Description
11128 ----------------  ---------------  ----------------------------------------------------------------------------------
11129 url               String           The url for the action (defaults to the form's url)
11130 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11131 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11132 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11133                                    validate the form on the client (defaults to false)
11134      * </pre>
11135      * @return {BasicForm} this
11136      */
11137     doAction : function(action, options){
11138         if(typeof action == 'string'){
11139             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11140         }
11141         if(this.fireEvent('beforeaction', this, action) !== false){
11142             this.beforeAction(action);
11143             action.run.defer(100, action);
11144         }
11145         return this;
11146     },
11147
11148     // private
11149     beforeAction : function(action){
11150         var o = action.options;
11151         
11152         if(this.loadMask){
11153             
11154             if(this.maskBody){
11155                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11156             } else {
11157                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11158             }
11159         }
11160         // not really supported yet.. ??
11161
11162         //if(this.waitMsgTarget === true){
11163         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11164         //}else if(this.waitMsgTarget){
11165         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11166         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11167         //}else {
11168         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11169        // }
11170
11171     },
11172
11173     // private
11174     afterAction : function(action, success){
11175         this.activeAction = null;
11176         var o = action.options;
11177
11178         if(this.loadMask){
11179             
11180             if(this.maskBody){
11181                 Roo.get(document.body).unmask();
11182             } else {
11183                 this.el.unmask();
11184             }
11185         }
11186         
11187         //if(this.waitMsgTarget === true){
11188 //            this.el.unmask();
11189         //}else if(this.waitMsgTarget){
11190         //    this.waitMsgTarget.unmask();
11191         //}else{
11192         //    Roo.MessageBox.updateProgress(1);
11193         //    Roo.MessageBox.hide();
11194        // }
11195         //
11196         if(success){
11197             if(o.reset){
11198                 this.reset();
11199             }
11200             Roo.callback(o.success, o.scope, [this, action]);
11201             this.fireEvent('actioncomplete', this, action);
11202
11203         }else{
11204
11205             // failure condition..
11206             // we have a scenario where updates need confirming.
11207             // eg. if a locking scenario exists..
11208             // we look for { errors : { needs_confirm : true }} in the response.
11209             if (
11210                 (typeof(action.result) != 'undefined')  &&
11211                 (typeof(action.result.errors) != 'undefined')  &&
11212                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11213            ){
11214                 var _t = this;
11215                 Roo.log("not supported yet");
11216                  /*
11217
11218                 Roo.MessageBox.confirm(
11219                     "Change requires confirmation",
11220                     action.result.errorMsg,
11221                     function(r) {
11222                         if (r != 'yes') {
11223                             return;
11224                         }
11225                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11226                     }
11227
11228                 );
11229                 */
11230
11231
11232                 return;
11233             }
11234
11235             Roo.callback(o.failure, o.scope, [this, action]);
11236             // show an error message if no failed handler is set..
11237             if (!this.hasListener('actionfailed')) {
11238                 Roo.log("need to add dialog support");
11239                 /*
11240                 Roo.MessageBox.alert("Error",
11241                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11242                         action.result.errorMsg :
11243                         "Saving Failed, please check your entries or try again"
11244                 );
11245                 */
11246             }
11247
11248             this.fireEvent('actionfailed', this, action);
11249         }
11250
11251     },
11252     /**
11253      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11254      * @param {String} id The value to search for
11255      * @return Field
11256      */
11257     findField : function(id){
11258         var items = this.getItems();
11259         var field = items.get(id);
11260         if(!field){
11261              items.each(function(f){
11262                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11263                     field = f;
11264                     return false;
11265                 }
11266                 return true;
11267             });
11268         }
11269         return field || null;
11270     },
11271      /**
11272      * Mark fields in this form invalid in bulk.
11273      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11274      * @return {BasicForm} this
11275      */
11276     markInvalid : function(errors){
11277         if(errors instanceof Array){
11278             for(var i = 0, len = errors.length; i < len; i++){
11279                 var fieldError = errors[i];
11280                 var f = this.findField(fieldError.id);
11281                 if(f){
11282                     f.markInvalid(fieldError.msg);
11283                 }
11284             }
11285         }else{
11286             var field, id;
11287             for(id in errors){
11288                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11289                     field.markInvalid(errors[id]);
11290                 }
11291             }
11292         }
11293         //Roo.each(this.childForms || [], function (f) {
11294         //    f.markInvalid(errors);
11295         //});
11296
11297         return this;
11298     },
11299
11300     /**
11301      * Set values for fields in this form in bulk.
11302      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11303      * @return {BasicForm} this
11304      */
11305     setValues : function(values){
11306         if(values instanceof Array){ // array of objects
11307             for(var i = 0, len = values.length; i < len; i++){
11308                 var v = values[i];
11309                 var f = this.findField(v.id);
11310                 if(f){
11311                     f.setValue(v.value);
11312                     if(this.trackResetOnLoad){
11313                         f.originalValue = f.getValue();
11314                     }
11315                 }
11316             }
11317         }else{ // object hash
11318             var field, id;
11319             for(id in values){
11320                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11321
11322                     if (field.setFromData &&
11323                         field.valueField &&
11324                         field.displayField &&
11325                         // combos' with local stores can
11326                         // be queried via setValue()
11327                         // to set their value..
11328                         (field.store && !field.store.isLocal)
11329                         ) {
11330                         // it's a combo
11331                         var sd = { };
11332                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11333                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11334                         field.setFromData(sd);
11335
11336                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11337                         
11338                         field.setFromData(values);
11339                         
11340                     } else {
11341                         field.setValue(values[id]);
11342                     }
11343
11344
11345                     if(this.trackResetOnLoad){
11346                         field.originalValue = field.getValue();
11347                     }
11348                 }
11349             }
11350         }
11351
11352         //Roo.each(this.childForms || [], function (f) {
11353         //    f.setValues(values);
11354         //});
11355
11356         return this;
11357     },
11358
11359     /**
11360      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11361      * they are returned as an array.
11362      * @param {Boolean} asString
11363      * @return {Object}
11364      */
11365     getValues : function(asString){
11366         //if (this.childForms) {
11367             // copy values from the child forms
11368         //    Roo.each(this.childForms, function (f) {
11369         //        this.setValues(f.getValues());
11370         //    }, this);
11371         //}
11372
11373
11374
11375         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11376         if(asString === true){
11377             return fs;
11378         }
11379         return Roo.urlDecode(fs);
11380     },
11381
11382     /**
11383      * Returns the fields in this form as an object with key/value pairs.
11384      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11385      * @return {Object}
11386      */
11387     getFieldValues : function(with_hidden)
11388     {
11389         var items = this.getItems();
11390         var ret = {};
11391         items.each(function(f){
11392             
11393             if (!f.getName()) {
11394                 return;
11395             }
11396             
11397             var v = f.getValue();
11398             
11399             if (f.inputType =='radio') {
11400                 if (typeof(ret[f.getName()]) == 'undefined') {
11401                     ret[f.getName()] = ''; // empty..
11402                 }
11403
11404                 if (!f.el.dom.checked) {
11405                     return;
11406
11407                 }
11408                 v = f.el.dom.value;
11409
11410             }
11411             
11412             if(f.xtype == 'MoneyField'){
11413                 ret[f.currencyName] = f.getCurrency();
11414             }
11415
11416             // not sure if this supported any more..
11417             if ((typeof(v) == 'object') && f.getRawValue) {
11418                 v = f.getRawValue() ; // dates..
11419             }
11420             // combo boxes where name != hiddenName...
11421             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11422                 ret[f.name] = f.getRawValue();
11423             }
11424             ret[f.getName()] = v;
11425         });
11426
11427         return ret;
11428     },
11429
11430     /**
11431      * Clears all invalid messages in this form.
11432      * @return {BasicForm} this
11433      */
11434     clearInvalid : function(){
11435         var items = this.getItems();
11436
11437         items.each(function(f){
11438            f.clearInvalid();
11439         });
11440
11441         return this;
11442     },
11443
11444     /**
11445      * Resets this form.
11446      * @return {BasicForm} this
11447      */
11448     reset : function(){
11449         var items = this.getItems();
11450         items.each(function(f){
11451             f.reset();
11452         });
11453
11454         Roo.each(this.childForms || [], function (f) {
11455             f.reset();
11456         });
11457
11458
11459         return this;
11460     },
11461     
11462     getItems : function()
11463     {
11464         var r=new Roo.util.MixedCollection(false, function(o){
11465             return o.id || (o.id = Roo.id());
11466         });
11467         var iter = function(el) {
11468             if (el.inputEl) {
11469                 r.add(el);
11470             }
11471             if (!el.items) {
11472                 return;
11473             }
11474             Roo.each(el.items,function(e) {
11475                 iter(e);
11476             });
11477         };
11478
11479         iter(this);
11480         return r;
11481     },
11482     
11483     hideFields : function(items)
11484     {
11485         Roo.each(items, function(i){
11486             
11487             var f = this.findField(i);
11488             
11489             if(!f){
11490                 return;
11491             }
11492             
11493             f.hide();
11494             
11495         }, this);
11496     },
11497     
11498     showFields : function(items)
11499     {
11500         Roo.each(items, function(i){
11501             
11502             var f = this.findField(i);
11503             
11504             if(!f){
11505                 return;
11506             }
11507             
11508             f.show();
11509             
11510         }, this);
11511     }
11512
11513 });
11514
11515 Roo.apply(Roo.bootstrap.Form, {
11516     
11517     popover : {
11518         
11519         padding : 5,
11520         
11521         isApplied : false,
11522         
11523         isMasked : false,
11524         
11525         form : false,
11526         
11527         target : false,
11528         
11529         toolTip : false,
11530         
11531         intervalID : false,
11532         
11533         maskEl : false,
11534         
11535         apply : function()
11536         {
11537             if(this.isApplied){
11538                 return;
11539             }
11540             
11541             this.maskEl = {
11542                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11543                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11544                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11545                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11546             };
11547             
11548             this.maskEl.top.enableDisplayMode("block");
11549             this.maskEl.left.enableDisplayMode("block");
11550             this.maskEl.bottom.enableDisplayMode("block");
11551             this.maskEl.right.enableDisplayMode("block");
11552             
11553             this.toolTip = new Roo.bootstrap.Tooltip({
11554                 cls : 'roo-form-error-popover',
11555                 alignment : {
11556                     'left' : ['r-l', [-2,0], 'right'],
11557                     'right' : ['l-r', [2,0], 'left'],
11558                     'bottom' : ['tl-bl', [0,2], 'top'],
11559                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11560                 }
11561             });
11562             
11563             this.toolTip.render(Roo.get(document.body));
11564
11565             this.toolTip.el.enableDisplayMode("block");
11566             
11567             Roo.get(document.body).on('click', function(){
11568                 this.unmask();
11569             }, this);
11570             
11571             Roo.get(document.body).on('touchstart', function(){
11572                 this.unmask();
11573             }, this);
11574             
11575             this.isApplied = true
11576         },
11577         
11578         mask : function(form, target)
11579         {
11580             this.form = form;
11581             
11582             this.target = target;
11583             
11584             if(!this.form.errorMask || !target.el){
11585                 return;
11586             }
11587             
11588             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11589             
11590             Roo.log(scrollable);
11591             
11592             var ot = this.target.el.calcOffsetsTo(scrollable);
11593             
11594             var scrollTo = ot[1] - this.form.maskOffset;
11595             
11596             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11597             
11598             scrollable.scrollTo('top', scrollTo);
11599             
11600             var box = this.target.el.getBox();
11601             Roo.log(box);
11602             var zIndex = Roo.bootstrap.Modal.zIndex++;
11603
11604             
11605             this.maskEl.top.setStyle('position', 'absolute');
11606             this.maskEl.top.setStyle('z-index', zIndex);
11607             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11608             this.maskEl.top.setLeft(0);
11609             this.maskEl.top.setTop(0);
11610             this.maskEl.top.show();
11611             
11612             this.maskEl.left.setStyle('position', 'absolute');
11613             this.maskEl.left.setStyle('z-index', zIndex);
11614             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11615             this.maskEl.left.setLeft(0);
11616             this.maskEl.left.setTop(box.y - this.padding);
11617             this.maskEl.left.show();
11618
11619             this.maskEl.bottom.setStyle('position', 'absolute');
11620             this.maskEl.bottom.setStyle('z-index', zIndex);
11621             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11622             this.maskEl.bottom.setLeft(0);
11623             this.maskEl.bottom.setTop(box.bottom + this.padding);
11624             this.maskEl.bottom.show();
11625
11626             this.maskEl.right.setStyle('position', 'absolute');
11627             this.maskEl.right.setStyle('z-index', zIndex);
11628             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11629             this.maskEl.right.setLeft(box.right + this.padding);
11630             this.maskEl.right.setTop(box.y - this.padding);
11631             this.maskEl.right.show();
11632
11633             this.toolTip.bindEl = this.target.el;
11634
11635             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11636
11637             var tip = this.target.blankText;
11638
11639             if(this.target.getValue() !== '' ) {
11640                 
11641                 if (this.target.invalidText.length) {
11642                     tip = this.target.invalidText;
11643                 } else if (this.target.regexText.length){
11644                     tip = this.target.regexText;
11645                 }
11646             }
11647
11648             this.toolTip.show(tip);
11649
11650             this.intervalID = window.setInterval(function() {
11651                 Roo.bootstrap.Form.popover.unmask();
11652             }, 10000);
11653
11654             window.onwheel = function(){ return false;};
11655             
11656             (function(){ this.isMasked = true; }).defer(500, this);
11657             
11658         },
11659         
11660         unmask : function()
11661         {
11662             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11663                 return;
11664             }
11665             
11666             this.maskEl.top.setStyle('position', 'absolute');
11667             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11668             this.maskEl.top.hide();
11669
11670             this.maskEl.left.setStyle('position', 'absolute');
11671             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11672             this.maskEl.left.hide();
11673
11674             this.maskEl.bottom.setStyle('position', 'absolute');
11675             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11676             this.maskEl.bottom.hide();
11677
11678             this.maskEl.right.setStyle('position', 'absolute');
11679             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11680             this.maskEl.right.hide();
11681             
11682             this.toolTip.hide();
11683             
11684             this.toolTip.el.hide();
11685             
11686             window.onwheel = function(){ return true;};
11687             
11688             if(this.intervalID){
11689                 window.clearInterval(this.intervalID);
11690                 this.intervalID = false;
11691             }
11692             
11693             this.isMasked = false;
11694             
11695         }
11696         
11697     }
11698     
11699 });
11700
11701 /*
11702  * Based on:
11703  * Ext JS Library 1.1.1
11704  * Copyright(c) 2006-2007, Ext JS, LLC.
11705  *
11706  * Originally Released Under LGPL - original licence link has changed is not relivant.
11707  *
11708  * Fork - LGPL
11709  * <script type="text/javascript">
11710  */
11711 /**
11712  * @class Roo.form.VTypes
11713  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11714  * @singleton
11715  */
11716 Roo.form.VTypes = function(){
11717     // closure these in so they are only created once.
11718     var alpha = /^[a-zA-Z_]+$/;
11719     var alphanum = /^[a-zA-Z0-9_]+$/;
11720     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11721     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11722
11723     // All these messages and functions are configurable
11724     return {
11725         /**
11726          * The function used to validate email addresses
11727          * @param {String} value The email address
11728          */
11729         'email' : function(v){
11730             return email.test(v);
11731         },
11732         /**
11733          * The error text to display when the email validation function returns false
11734          * @type String
11735          */
11736         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11737         /**
11738          * The keystroke filter mask to be applied on email input
11739          * @type RegExp
11740          */
11741         'emailMask' : /[a-z0-9_\.\-@]/i,
11742
11743         /**
11744          * The function used to validate URLs
11745          * @param {String} value The URL
11746          */
11747         'url' : function(v){
11748             return url.test(v);
11749         },
11750         /**
11751          * The error text to display when the url validation function returns false
11752          * @type String
11753          */
11754         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11755         
11756         /**
11757          * The function used to validate alpha values
11758          * @param {String} value The value
11759          */
11760         'alpha' : function(v){
11761             return alpha.test(v);
11762         },
11763         /**
11764          * The error text to display when the alpha validation function returns false
11765          * @type String
11766          */
11767         'alphaText' : 'This field should only contain letters and _',
11768         /**
11769          * The keystroke filter mask to be applied on alpha input
11770          * @type RegExp
11771          */
11772         'alphaMask' : /[a-z_]/i,
11773
11774         /**
11775          * The function used to validate alphanumeric values
11776          * @param {String} value The value
11777          */
11778         'alphanum' : function(v){
11779             return alphanum.test(v);
11780         },
11781         /**
11782          * The error text to display when the alphanumeric validation function returns false
11783          * @type String
11784          */
11785         'alphanumText' : 'This field should only contain letters, numbers and _',
11786         /**
11787          * The keystroke filter mask to be applied on alphanumeric input
11788          * @type RegExp
11789          */
11790         'alphanumMask' : /[a-z0-9_]/i
11791     };
11792 }();/*
11793  * - LGPL
11794  *
11795  * Input
11796  * 
11797  */
11798
11799 /**
11800  * @class Roo.bootstrap.Input
11801  * @extends Roo.bootstrap.Component
11802  * Bootstrap Input class
11803  * @cfg {Boolean} disabled is it disabled
11804  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11805  * @cfg {String} name name of the input
11806  * @cfg {string} fieldLabel - the label associated
11807  * @cfg {string} placeholder - placeholder to put in text.
11808  * @cfg {string}  before - input group add on before
11809  * @cfg {string} after - input group add on after
11810  * @cfg {string} size - (lg|sm) or leave empty..
11811  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11812  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11813  * @cfg {Number} md colspan out of 12 for computer-sized screens
11814  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11815  * @cfg {string} value default value of the input
11816  * @cfg {Number} labelWidth set the width of label 
11817  * @cfg {Number} labellg set the width of label (1-12)
11818  * @cfg {Number} labelmd set the width of label (1-12)
11819  * @cfg {Number} labelsm set the width of label (1-12)
11820  * @cfg {Number} labelxs set the width of label (1-12)
11821  * @cfg {String} labelAlign (top|left)
11822  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11823  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11824  * @cfg {String} indicatorpos (left|right) default left
11825  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11826  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11827  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11828
11829  * @cfg {String} align (left|center|right) Default left
11830  * @cfg {Boolean} forceFeedback (true|false) Default false
11831  * 
11832  * @constructor
11833  * Create a new Input
11834  * @param {Object} config The config object
11835  */
11836
11837 Roo.bootstrap.Input = function(config){
11838     
11839     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11840     
11841     this.addEvents({
11842         /**
11843          * @event focus
11844          * Fires when this field receives input focus.
11845          * @param {Roo.form.Field} this
11846          */
11847         focus : true,
11848         /**
11849          * @event blur
11850          * Fires when this field loses input focus.
11851          * @param {Roo.form.Field} this
11852          */
11853         blur : true,
11854         /**
11855          * @event specialkey
11856          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11857          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11858          * @param {Roo.form.Field} this
11859          * @param {Roo.EventObject} e The event object
11860          */
11861         specialkey : true,
11862         /**
11863          * @event change
11864          * Fires just before the field blurs if the field value has changed.
11865          * @param {Roo.form.Field} this
11866          * @param {Mixed} newValue The new value
11867          * @param {Mixed} oldValue The original value
11868          */
11869         change : true,
11870         /**
11871          * @event invalid
11872          * Fires after the field has been marked as invalid.
11873          * @param {Roo.form.Field} this
11874          * @param {String} msg The validation message
11875          */
11876         invalid : true,
11877         /**
11878          * @event valid
11879          * Fires after the field has been validated with no errors.
11880          * @param {Roo.form.Field} this
11881          */
11882         valid : true,
11883          /**
11884          * @event keyup
11885          * Fires after the key up
11886          * @param {Roo.form.Field} this
11887          * @param {Roo.EventObject}  e The event Object
11888          */
11889         keyup : true,
11890         /**
11891          * @event paste
11892          * Fires after the user pastes into input
11893          * @param {Roo.form.Field} this
11894          * @param {Roo.EventObject}  e The event Object
11895          */
11896         paste : true
11897     });
11898 };
11899
11900 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11901      /**
11902      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11903       automatic validation (defaults to "keyup").
11904      */
11905     validationEvent : "keyup",
11906      /**
11907      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11908      */
11909     validateOnBlur : true,
11910     /**
11911      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11912      */
11913     validationDelay : 250,
11914      /**
11915      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11916      */
11917     focusClass : "x-form-focus",  // not needed???
11918     
11919        
11920     /**
11921      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11922      */
11923     invalidClass : "has-warning",
11924     
11925     /**
11926      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11927      */
11928     validClass : "has-success",
11929     
11930     /**
11931      * @cfg {Boolean} hasFeedback (true|false) default true
11932      */
11933     hasFeedback : true,
11934     
11935     /**
11936      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11937      */
11938     invalidFeedbackClass : "glyphicon-warning-sign",
11939     
11940     /**
11941      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11942      */
11943     validFeedbackClass : "glyphicon-ok",
11944     
11945     /**
11946      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11947      */
11948     selectOnFocus : false,
11949     
11950      /**
11951      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11952      */
11953     maskRe : null,
11954        /**
11955      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11956      */
11957     vtype : null,
11958     
11959       /**
11960      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11961      */
11962     disableKeyFilter : false,
11963     
11964        /**
11965      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11966      */
11967     disabled : false,
11968      /**
11969      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11970      */
11971     allowBlank : true,
11972     /**
11973      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11974      */
11975     blankText : "Please complete this mandatory field",
11976     
11977      /**
11978      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11979      */
11980     minLength : 0,
11981     /**
11982      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11983      */
11984     maxLength : Number.MAX_VALUE,
11985     /**
11986      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11987      */
11988     minLengthText : "The minimum length for this field is {0}",
11989     /**
11990      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11991      */
11992     maxLengthText : "The maximum length for this field is {0}",
11993   
11994     
11995     /**
11996      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11997      * If available, this function will be called only after the basic validators all return true, and will be passed the
11998      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11999      */
12000     validator : null,
12001     /**
12002      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12003      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12004      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12005      */
12006     regex : null,
12007     /**
12008      * @cfg {String} regexText -- Depricated - use Invalid Text
12009      */
12010     regexText : "",
12011     
12012     /**
12013      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12014      */
12015     invalidText : "",
12016     
12017     
12018     
12019     autocomplete: false,
12020     
12021     
12022     fieldLabel : '',
12023     inputType : 'text',
12024     
12025     name : false,
12026     placeholder: false,
12027     before : false,
12028     after : false,
12029     size : false,
12030     hasFocus : false,
12031     preventMark: false,
12032     isFormField : true,
12033     value : '',
12034     labelWidth : 2,
12035     labelAlign : false,
12036     readOnly : false,
12037     align : false,
12038     formatedValue : false,
12039     forceFeedback : false,
12040     
12041     indicatorpos : 'left',
12042     
12043     labellg : 0,
12044     labelmd : 0,
12045     labelsm : 0,
12046     labelxs : 0,
12047     
12048     capture : '',
12049     accept : '',
12050     
12051     parentLabelAlign : function()
12052     {
12053         var parent = this;
12054         while (parent.parent()) {
12055             parent = parent.parent();
12056             if (typeof(parent.labelAlign) !='undefined') {
12057                 return parent.labelAlign;
12058             }
12059         }
12060         return 'left';
12061         
12062     },
12063     
12064     getAutoCreate : function()
12065     {
12066         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12067         
12068         var id = Roo.id();
12069         
12070         var cfg = {};
12071         
12072         if(this.inputType != 'hidden'){
12073             cfg.cls = 'form-group' //input-group
12074         }
12075         
12076         var input =  {
12077             tag: 'input',
12078             id : id,
12079             type : this.inputType,
12080             value : this.value,
12081             cls : 'form-control',
12082             placeholder : this.placeholder || '',
12083             autocomplete : this.autocomplete || 'new-password'
12084         };
12085         if (this.inputType == 'file') {
12086             input.style = 'overflow:hidden'; // why not in CSS?
12087         }
12088         
12089         if(this.capture.length){
12090             input.capture = this.capture;
12091         }
12092         
12093         if(this.accept.length){
12094             input.accept = this.accept + "/*";
12095         }
12096         
12097         if(this.align){
12098             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12099         }
12100         
12101         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12102             input.maxLength = this.maxLength;
12103         }
12104         
12105         if (this.disabled) {
12106             input.disabled=true;
12107         }
12108         
12109         if (this.readOnly) {
12110             input.readonly=true;
12111         }
12112         
12113         if (this.name) {
12114             input.name = this.name;
12115         }
12116         
12117         if (this.size) {
12118             input.cls += ' input-' + this.size;
12119         }
12120         
12121         var settings=this;
12122         ['xs','sm','md','lg'].map(function(size){
12123             if (settings[size]) {
12124                 cfg.cls += ' col-' + size + '-' + settings[size];
12125             }
12126         });
12127         
12128         var inputblock = input;
12129         
12130         var feedback = {
12131             tag: 'span',
12132             cls: 'glyphicon form-control-feedback'
12133         };
12134             
12135         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12136             
12137             inputblock = {
12138                 cls : 'has-feedback',
12139                 cn :  [
12140                     input,
12141                     feedback
12142                 ] 
12143             };  
12144         }
12145         
12146         if (this.before || this.after) {
12147             
12148             inputblock = {
12149                 cls : 'input-group',
12150                 cn :  [] 
12151             };
12152             
12153             if (this.before && typeof(this.before) == 'string') {
12154                 
12155                 inputblock.cn.push({
12156                     tag :'span',
12157                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12158                     html : this.before
12159                 });
12160             }
12161             if (this.before && typeof(this.before) == 'object') {
12162                 this.before = Roo.factory(this.before);
12163                 
12164                 inputblock.cn.push({
12165                     tag :'span',
12166                     cls : 'roo-input-before input-group-prepend   input-group-' +
12167                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12168                 });
12169             }
12170             
12171             inputblock.cn.push(input);
12172             
12173             if (this.after && typeof(this.after) == 'string') {
12174                 inputblock.cn.push({
12175                     tag :'span',
12176                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12177                     html : this.after
12178                 });
12179             }
12180             if (this.after && typeof(this.after) == 'object') {
12181                 this.after = Roo.factory(this.after);
12182                 
12183                 inputblock.cn.push({
12184                     tag :'span',
12185                     cls : 'roo-input-after input-group-append  input-group-' +
12186                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12187                 });
12188             }
12189             
12190             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12191                 inputblock.cls += ' has-feedback';
12192                 inputblock.cn.push(feedback);
12193             }
12194         };
12195         var indicator = {
12196             tag : 'i',
12197             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12198             tooltip : 'This field is required'
12199         };
12200         if (this.allowBlank ) {
12201             indicator.style = this.allowBlank ? ' display:none' : '';
12202         }
12203         if (align ==='left' && this.fieldLabel.length) {
12204             
12205             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12206             
12207             cfg.cn = [
12208                 indicator,
12209                 {
12210                     tag: 'label',
12211                     'for' :  id,
12212                     cls : 'control-label col-form-label',
12213                     html : this.fieldLabel
12214
12215                 },
12216                 {
12217                     cls : "", 
12218                     cn: [
12219                         inputblock
12220                     ]
12221                 }
12222             ];
12223             
12224             var labelCfg = cfg.cn[1];
12225             var contentCfg = cfg.cn[2];
12226             
12227             if(this.indicatorpos == 'right'){
12228                 cfg.cn = [
12229                     {
12230                         tag: 'label',
12231                         'for' :  id,
12232                         cls : 'control-label col-form-label',
12233                         cn : [
12234                             {
12235                                 tag : 'span',
12236                                 html : this.fieldLabel
12237                             },
12238                             indicator
12239                         ]
12240                     },
12241                     {
12242                         cls : "",
12243                         cn: [
12244                             inputblock
12245                         ]
12246                     }
12247
12248                 ];
12249                 
12250                 labelCfg = cfg.cn[0];
12251                 contentCfg = cfg.cn[1];
12252             
12253             }
12254             
12255             if(this.labelWidth > 12){
12256                 labelCfg.style = "width: " + this.labelWidth + 'px';
12257             }
12258             
12259             if(this.labelWidth < 13 && this.labelmd == 0){
12260                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12261             }
12262             
12263             if(this.labellg > 0){
12264                 labelCfg.cls += ' col-lg-' + this.labellg;
12265                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12266             }
12267             
12268             if(this.labelmd > 0){
12269                 labelCfg.cls += ' col-md-' + this.labelmd;
12270                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12271             }
12272             
12273             if(this.labelsm > 0){
12274                 labelCfg.cls += ' col-sm-' + this.labelsm;
12275                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12276             }
12277             
12278             if(this.labelxs > 0){
12279                 labelCfg.cls += ' col-xs-' + this.labelxs;
12280                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12281             }
12282             
12283             
12284         } else if ( this.fieldLabel.length) {
12285                 
12286             
12287             
12288             cfg.cn = [
12289                 {
12290                     tag : 'i',
12291                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12292                     tooltip : 'This field is required',
12293                     style : this.allowBlank ? ' display:none' : '' 
12294                 },
12295                 {
12296                     tag: 'label',
12297                    //cls : 'input-group-addon',
12298                     html : this.fieldLabel
12299
12300                 },
12301
12302                inputblock
12303
12304            ];
12305            
12306            if(this.indicatorpos == 'right'){
12307        
12308                 cfg.cn = [
12309                     {
12310                         tag: 'label',
12311                        //cls : 'input-group-addon',
12312                         html : this.fieldLabel
12313
12314                     },
12315                     {
12316                         tag : 'i',
12317                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12318                         tooltip : 'This field is required',
12319                         style : this.allowBlank ? ' display:none' : '' 
12320                     },
12321
12322                    inputblock
12323
12324                ];
12325
12326             }
12327
12328         } else {
12329             
12330             cfg.cn = [
12331
12332                     inputblock
12333
12334             ];
12335                 
12336                 
12337         };
12338         
12339         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12340            cfg.cls += ' navbar-form';
12341         }
12342         
12343         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12344             // on BS4 we do this only if not form 
12345             cfg.cls += ' navbar-form';
12346             cfg.tag = 'li';
12347         }
12348         
12349         return cfg;
12350         
12351     },
12352     /**
12353      * return the real input element.
12354      */
12355     inputEl: function ()
12356     {
12357         return this.el.select('input.form-control',true).first();
12358     },
12359     
12360     tooltipEl : function()
12361     {
12362         return this.inputEl();
12363     },
12364     
12365     indicatorEl : function()
12366     {
12367         if (Roo.bootstrap.version == 4) {
12368             return false; // not enabled in v4 yet.
12369         }
12370         
12371         var indicator = this.el.select('i.roo-required-indicator',true).first();
12372         
12373         if(!indicator){
12374             return false;
12375         }
12376         
12377         return indicator;
12378         
12379     },
12380     
12381     setDisabled : function(v)
12382     {
12383         var i  = this.inputEl().dom;
12384         if (!v) {
12385             i.removeAttribute('disabled');
12386             return;
12387             
12388         }
12389         i.setAttribute('disabled','true');
12390     },
12391     initEvents : function()
12392     {
12393           
12394         this.inputEl().on("keydown" , this.fireKey,  this);
12395         this.inputEl().on("focus", this.onFocus,  this);
12396         this.inputEl().on("blur", this.onBlur,  this);
12397         
12398         this.inputEl().relayEvent('keyup', this);
12399         this.inputEl().relayEvent('paste', this);
12400         
12401         this.indicator = this.indicatorEl();
12402         
12403         if(this.indicator){
12404             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12405         }
12406  
12407         // reference to original value for reset
12408         this.originalValue = this.getValue();
12409         //Roo.form.TextField.superclass.initEvents.call(this);
12410         if(this.validationEvent == 'keyup'){
12411             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12412             this.inputEl().on('keyup', this.filterValidation, this);
12413         }
12414         else if(this.validationEvent !== false){
12415             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12416         }
12417         
12418         if(this.selectOnFocus){
12419             this.on("focus", this.preFocus, this);
12420             
12421         }
12422         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12423             this.inputEl().on("keypress", this.filterKeys, this);
12424         } else {
12425             this.inputEl().relayEvent('keypress', this);
12426         }
12427        /* if(this.grow){
12428             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12429             this.el.on("click", this.autoSize,  this);
12430         }
12431         */
12432         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12433             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12434         }
12435         
12436         if (typeof(this.before) == 'object') {
12437             this.before.render(this.el.select('.roo-input-before',true).first());
12438         }
12439         if (typeof(this.after) == 'object') {
12440             this.after.render(this.el.select('.roo-input-after',true).first());
12441         }
12442         
12443         this.inputEl().on('change', this.onChange, this);
12444         
12445     },
12446     filterValidation : function(e){
12447         if(!e.isNavKeyPress()){
12448             this.validationTask.delay(this.validationDelay);
12449         }
12450     },
12451      /**
12452      * Validates the field value
12453      * @return {Boolean} True if the value is valid, else false
12454      */
12455     validate : function(){
12456         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12457         if(this.disabled || this.validateValue(this.getRawValue())){
12458             this.markValid();
12459             return true;
12460         }
12461         
12462         this.markInvalid();
12463         return false;
12464     },
12465     
12466     
12467     /**
12468      * Validates a value according to the field's validation rules and marks the field as invalid
12469      * if the validation fails
12470      * @param {Mixed} value The value to validate
12471      * @return {Boolean} True if the value is valid, else false
12472      */
12473     validateValue : function(value)
12474     {
12475         if(this.getVisibilityEl().hasClass('hidden')){
12476             return true;
12477         }
12478         
12479         if(value.length < 1)  { // if it's blank
12480             if(this.allowBlank){
12481                 return true;
12482             }
12483             return false;
12484         }
12485         
12486         if(value.length < this.minLength){
12487             return false;
12488         }
12489         if(value.length > this.maxLength){
12490             return false;
12491         }
12492         if(this.vtype){
12493             var vt = Roo.form.VTypes;
12494             if(!vt[this.vtype](value, this)){
12495                 return false;
12496             }
12497         }
12498         if(typeof this.validator == "function"){
12499             var msg = this.validator(value);
12500             if(msg !== true){
12501                 return false;
12502             }
12503             if (typeof(msg) == 'string') {
12504                 this.invalidText = msg;
12505             }
12506         }
12507         
12508         if(this.regex && !this.regex.test(value)){
12509             return false;
12510         }
12511         
12512         return true;
12513     },
12514     
12515      // private
12516     fireKey : function(e){
12517         //Roo.log('field ' + e.getKey());
12518         if(e.isNavKeyPress()){
12519             this.fireEvent("specialkey", this, e);
12520         }
12521     },
12522     focus : function (selectText){
12523         if(this.rendered){
12524             this.inputEl().focus();
12525             if(selectText === true){
12526                 this.inputEl().dom.select();
12527             }
12528         }
12529         return this;
12530     } ,
12531     
12532     onFocus : function(){
12533         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12534            // this.el.addClass(this.focusClass);
12535         }
12536         if(!this.hasFocus){
12537             this.hasFocus = true;
12538             this.startValue = this.getValue();
12539             this.fireEvent("focus", this);
12540         }
12541     },
12542     
12543     beforeBlur : Roo.emptyFn,
12544
12545     
12546     // private
12547     onBlur : function(){
12548         this.beforeBlur();
12549         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12550             //this.el.removeClass(this.focusClass);
12551         }
12552         this.hasFocus = false;
12553         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12554             this.validate();
12555         }
12556         var v = this.getValue();
12557         if(String(v) !== String(this.startValue)){
12558             this.fireEvent('change', this, v, this.startValue);
12559         }
12560         this.fireEvent("blur", this);
12561     },
12562     
12563     onChange : function(e)
12564     {
12565         var v = this.getValue();
12566         if(String(v) !== String(this.startValue)){
12567             this.fireEvent('change', this, v, this.startValue);
12568         }
12569         
12570     },
12571     
12572     /**
12573      * Resets the current field value to the originally loaded value and clears any validation messages
12574      */
12575     reset : function(){
12576         this.setValue(this.originalValue);
12577         this.validate();
12578     },
12579      /**
12580      * Returns the name of the field
12581      * @return {Mixed} name The name field
12582      */
12583     getName: function(){
12584         return this.name;
12585     },
12586      /**
12587      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12588      * @return {Mixed} value The field value
12589      */
12590     getValue : function(){
12591         
12592         var v = this.inputEl().getValue();
12593         
12594         return v;
12595     },
12596     /**
12597      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12598      * @return {Mixed} value The field value
12599      */
12600     getRawValue : function(){
12601         var v = this.inputEl().getValue();
12602         
12603         return v;
12604     },
12605     
12606     /**
12607      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12608      * @param {Mixed} value The value to set
12609      */
12610     setRawValue : function(v){
12611         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12612     },
12613     
12614     selectText : function(start, end){
12615         var v = this.getRawValue();
12616         if(v.length > 0){
12617             start = start === undefined ? 0 : start;
12618             end = end === undefined ? v.length : end;
12619             var d = this.inputEl().dom;
12620             if(d.setSelectionRange){
12621                 d.setSelectionRange(start, end);
12622             }else if(d.createTextRange){
12623                 var range = d.createTextRange();
12624                 range.moveStart("character", start);
12625                 range.moveEnd("character", v.length-end);
12626                 range.select();
12627             }
12628         }
12629     },
12630     
12631     /**
12632      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12633      * @param {Mixed} value The value to set
12634      */
12635     setValue : function(v){
12636         this.value = v;
12637         if(this.rendered){
12638             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12639             this.validate();
12640         }
12641     },
12642     
12643     /*
12644     processValue : function(value){
12645         if(this.stripCharsRe){
12646             var newValue = value.replace(this.stripCharsRe, '');
12647             if(newValue !== value){
12648                 this.setRawValue(newValue);
12649                 return newValue;
12650             }
12651         }
12652         return value;
12653     },
12654   */
12655     preFocus : function(){
12656         
12657         if(this.selectOnFocus){
12658             this.inputEl().dom.select();
12659         }
12660     },
12661     filterKeys : function(e){
12662         var k = e.getKey();
12663         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12664             return;
12665         }
12666         var c = e.getCharCode(), cc = String.fromCharCode(c);
12667         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12668             return;
12669         }
12670         if(!this.maskRe.test(cc)){
12671             e.stopEvent();
12672         }
12673     },
12674      /**
12675      * Clear any invalid styles/messages for this field
12676      */
12677     clearInvalid : function(){
12678         
12679         if(!this.el || this.preventMark){ // not rendered
12680             return;
12681         }
12682         
12683         
12684         this.el.removeClass([this.invalidClass, 'is-invalid']);
12685         
12686         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12687             
12688             var feedback = this.el.select('.form-control-feedback', true).first();
12689             
12690             if(feedback){
12691                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12692             }
12693             
12694         }
12695         
12696         if(this.indicator){
12697             this.indicator.removeClass('visible');
12698             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12699         }
12700         
12701         this.fireEvent('valid', this);
12702     },
12703     
12704      /**
12705      * Mark this field as valid
12706      */
12707     markValid : function()
12708     {
12709         if(!this.el  || this.preventMark){ // not rendered...
12710             return;
12711         }
12712         
12713         this.el.removeClass([this.invalidClass, this.validClass]);
12714         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12715
12716         var feedback = this.el.select('.form-control-feedback', true).first();
12717             
12718         if(feedback){
12719             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12720         }
12721         
12722         if(this.indicator){
12723             this.indicator.removeClass('visible');
12724             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12725         }
12726         
12727         if(this.disabled){
12728             return;
12729         }
12730         
12731            
12732         if(this.allowBlank && !this.getRawValue().length){
12733             return;
12734         }
12735         if (Roo.bootstrap.version == 3) {
12736             this.el.addClass(this.validClass);
12737         } else {
12738             this.inputEl().addClass('is-valid');
12739         }
12740
12741         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12742             
12743             var feedback = this.el.select('.form-control-feedback', true).first();
12744             
12745             if(feedback){
12746                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12747                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12748             }
12749             
12750         }
12751         
12752         this.fireEvent('valid', this);
12753     },
12754     
12755      /**
12756      * Mark this field as invalid
12757      * @param {String} msg The validation message
12758      */
12759     markInvalid : function(msg)
12760     {
12761         if(!this.el  || this.preventMark){ // not rendered
12762             return;
12763         }
12764         
12765         this.el.removeClass([this.invalidClass, this.validClass]);
12766         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12767         
12768         var feedback = this.el.select('.form-control-feedback', true).first();
12769             
12770         if(feedback){
12771             this.el.select('.form-control-feedback', true).first().removeClass(
12772                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12773         }
12774
12775         if(this.disabled){
12776             return;
12777         }
12778         
12779         if(this.allowBlank && !this.getRawValue().length){
12780             return;
12781         }
12782         
12783         if(this.indicator){
12784             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12785             this.indicator.addClass('visible');
12786         }
12787         if (Roo.bootstrap.version == 3) {
12788             this.el.addClass(this.invalidClass);
12789         } else {
12790             this.inputEl().addClass('is-invalid');
12791         }
12792         
12793         
12794         
12795         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12796             
12797             var feedback = this.el.select('.form-control-feedback', true).first();
12798             
12799             if(feedback){
12800                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12801                 
12802                 if(this.getValue().length || this.forceFeedback){
12803                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12804                 }
12805                 
12806             }
12807             
12808         }
12809         
12810         this.fireEvent('invalid', this, msg);
12811     },
12812     // private
12813     SafariOnKeyDown : function(event)
12814     {
12815         // this is a workaround for a password hang bug on chrome/ webkit.
12816         if (this.inputEl().dom.type != 'password') {
12817             return;
12818         }
12819         
12820         var isSelectAll = false;
12821         
12822         if(this.inputEl().dom.selectionEnd > 0){
12823             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12824         }
12825         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12826             event.preventDefault();
12827             this.setValue('');
12828             return;
12829         }
12830         
12831         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12832             
12833             event.preventDefault();
12834             // this is very hacky as keydown always get's upper case.
12835             //
12836             var cc = String.fromCharCode(event.getCharCode());
12837             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12838             
12839         }
12840     },
12841     adjustWidth : function(tag, w){
12842         tag = tag.toLowerCase();
12843         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12844             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12845                 if(tag == 'input'){
12846                     return w + 2;
12847                 }
12848                 if(tag == 'textarea'){
12849                     return w-2;
12850                 }
12851             }else if(Roo.isOpera){
12852                 if(tag == 'input'){
12853                     return w + 2;
12854                 }
12855                 if(tag == 'textarea'){
12856                     return w-2;
12857                 }
12858             }
12859         }
12860         return w;
12861     },
12862     
12863     setFieldLabel : function(v)
12864     {
12865         if(!this.rendered){
12866             return;
12867         }
12868         
12869         if(this.indicatorEl()){
12870             var ar = this.el.select('label > span',true);
12871             
12872             if (ar.elements.length) {
12873                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12874                 this.fieldLabel = v;
12875                 return;
12876             }
12877             
12878             var br = this.el.select('label',true);
12879             
12880             if(br.elements.length) {
12881                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12882                 this.fieldLabel = v;
12883                 return;
12884             }
12885             
12886             Roo.log('Cannot Found any of label > span || label in input');
12887             return;
12888         }
12889         
12890         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12891         this.fieldLabel = v;
12892         
12893         
12894     }
12895 });
12896
12897  
12898 /*
12899  * - LGPL
12900  *
12901  * Input
12902  * 
12903  */
12904
12905 /**
12906  * @class Roo.bootstrap.TextArea
12907  * @extends Roo.bootstrap.Input
12908  * Bootstrap TextArea class
12909  * @cfg {Number} cols Specifies the visible width of a text area
12910  * @cfg {Number} rows Specifies the visible number of lines in a text area
12911  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12912  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12913  * @cfg {string} html text
12914  * 
12915  * @constructor
12916  * Create a new TextArea
12917  * @param {Object} config The config object
12918  */
12919
12920 Roo.bootstrap.TextArea = function(config){
12921     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12922    
12923 };
12924
12925 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12926      
12927     cols : false,
12928     rows : 5,
12929     readOnly : false,
12930     warp : 'soft',
12931     resize : false,
12932     value: false,
12933     html: false,
12934     
12935     getAutoCreate : function(){
12936         
12937         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12938         
12939         var id = Roo.id();
12940         
12941         var cfg = {};
12942         
12943         if(this.inputType != 'hidden'){
12944             cfg.cls = 'form-group' //input-group
12945         }
12946         
12947         var input =  {
12948             tag: 'textarea',
12949             id : id,
12950             warp : this.warp,
12951             rows : this.rows,
12952             value : this.value || '',
12953             html: this.html || '',
12954             cls : 'form-control',
12955             placeholder : this.placeholder || '' 
12956             
12957         };
12958         
12959         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12960             input.maxLength = this.maxLength;
12961         }
12962         
12963         if(this.resize){
12964             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12965         }
12966         
12967         if(this.cols){
12968             input.cols = this.cols;
12969         }
12970         
12971         if (this.readOnly) {
12972             input.readonly = true;
12973         }
12974         
12975         if (this.name) {
12976             input.name = this.name;
12977         }
12978         
12979         if (this.size) {
12980             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12981         }
12982         
12983         var settings=this;
12984         ['xs','sm','md','lg'].map(function(size){
12985             if (settings[size]) {
12986                 cfg.cls += ' col-' + size + '-' + settings[size];
12987             }
12988         });
12989         
12990         var inputblock = input;
12991         
12992         if(this.hasFeedback && !this.allowBlank){
12993             
12994             var feedback = {
12995                 tag: 'span',
12996                 cls: 'glyphicon form-control-feedback'
12997             };
12998
12999             inputblock = {
13000                 cls : 'has-feedback',
13001                 cn :  [
13002                     input,
13003                     feedback
13004                 ] 
13005             };  
13006         }
13007         
13008         
13009         if (this.before || this.after) {
13010             
13011             inputblock = {
13012                 cls : 'input-group',
13013                 cn :  [] 
13014             };
13015             if (this.before) {
13016                 inputblock.cn.push({
13017                     tag :'span',
13018                     cls : 'input-group-addon',
13019                     html : this.before
13020                 });
13021             }
13022             
13023             inputblock.cn.push(input);
13024             
13025             if(this.hasFeedback && !this.allowBlank){
13026                 inputblock.cls += ' has-feedback';
13027                 inputblock.cn.push(feedback);
13028             }
13029             
13030             if (this.after) {
13031                 inputblock.cn.push({
13032                     tag :'span',
13033                     cls : 'input-group-addon',
13034                     html : this.after
13035                 });
13036             }
13037             
13038         }
13039         
13040         if (align ==='left' && this.fieldLabel.length) {
13041             cfg.cn = [
13042                 {
13043                     tag: 'label',
13044                     'for' :  id,
13045                     cls : 'control-label',
13046                     html : this.fieldLabel
13047                 },
13048                 {
13049                     cls : "",
13050                     cn: [
13051                         inputblock
13052                     ]
13053                 }
13054
13055             ];
13056             
13057             if(this.labelWidth > 12){
13058                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13059             }
13060
13061             if(this.labelWidth < 13 && this.labelmd == 0){
13062                 this.labelmd = this.labelWidth;
13063             }
13064
13065             if(this.labellg > 0){
13066                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13067                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13068             }
13069
13070             if(this.labelmd > 0){
13071                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13072                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13073             }
13074
13075             if(this.labelsm > 0){
13076                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13077                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13078             }
13079
13080             if(this.labelxs > 0){
13081                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13082                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13083             }
13084             
13085         } else if ( this.fieldLabel.length) {
13086             cfg.cn = [
13087
13088                {
13089                    tag: 'label',
13090                    //cls : 'input-group-addon',
13091                    html : this.fieldLabel
13092
13093                },
13094
13095                inputblock
13096
13097            ];
13098
13099         } else {
13100
13101             cfg.cn = [
13102
13103                 inputblock
13104
13105             ];
13106                 
13107         }
13108         
13109         if (this.disabled) {
13110             input.disabled=true;
13111         }
13112         
13113         return cfg;
13114         
13115     },
13116     /**
13117      * return the real textarea element.
13118      */
13119     inputEl: function ()
13120     {
13121         return this.el.select('textarea.form-control',true).first();
13122     },
13123     
13124     /**
13125      * Clear any invalid styles/messages for this field
13126      */
13127     clearInvalid : function()
13128     {
13129         
13130         if(!this.el || this.preventMark){ // not rendered
13131             return;
13132         }
13133         
13134         var label = this.el.select('label', true).first();
13135         var icon = this.el.select('i.fa-star', true).first();
13136         
13137         if(label && icon){
13138             icon.remove();
13139         }
13140         this.el.removeClass( this.validClass);
13141         this.inputEl().removeClass('is-invalid');
13142          
13143         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13144             
13145             var feedback = this.el.select('.form-control-feedback', true).first();
13146             
13147             if(feedback){
13148                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13149             }
13150             
13151         }
13152         
13153         this.fireEvent('valid', this);
13154     },
13155     
13156      /**
13157      * Mark this field as valid
13158      */
13159     markValid : function()
13160     {
13161         if(!this.el  || this.preventMark){ // not rendered
13162             return;
13163         }
13164         
13165         this.el.removeClass([this.invalidClass, this.validClass]);
13166         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13167         
13168         var feedback = this.el.select('.form-control-feedback', true).first();
13169             
13170         if(feedback){
13171             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13172         }
13173
13174         if(this.disabled || this.allowBlank){
13175             return;
13176         }
13177         
13178         var label = this.el.select('label', true).first();
13179         var icon = this.el.select('i.fa-star', true).first();
13180         
13181         if(label && icon){
13182             icon.remove();
13183         }
13184         if (Roo.bootstrap.version == 3) {
13185             this.el.addClass(this.validClass);
13186         } else {
13187             this.inputEl().addClass('is-valid');
13188         }
13189         
13190         
13191         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13192             
13193             var feedback = this.el.select('.form-control-feedback', true).first();
13194             
13195             if(feedback){
13196                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13197                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13198             }
13199             
13200         }
13201         
13202         this.fireEvent('valid', this);
13203     },
13204     
13205      /**
13206      * Mark this field as invalid
13207      * @param {String} msg The validation message
13208      */
13209     markInvalid : function(msg)
13210     {
13211         if(!this.el  || this.preventMark){ // not rendered
13212             return;
13213         }
13214         
13215         this.el.removeClass([this.invalidClass, this.validClass]);
13216         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13217         
13218         var feedback = this.el.select('.form-control-feedback', true).first();
13219             
13220         if(feedback){
13221             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13222         }
13223
13224         if(this.disabled || this.allowBlank){
13225             return;
13226         }
13227         
13228         var label = this.el.select('label', true).first();
13229         var icon = this.el.select('i.fa-star', true).first();
13230         
13231         if(!this.getValue().length && label && !icon){
13232             this.el.createChild({
13233                 tag : 'i',
13234                 cls : 'text-danger fa fa-lg fa-star',
13235                 tooltip : 'This field is required',
13236                 style : 'margin-right:5px;'
13237             }, label, true);
13238         }
13239         
13240         if (Roo.bootstrap.version == 3) {
13241             this.el.addClass(this.invalidClass);
13242         } else {
13243             this.inputEl().addClass('is-invalid');
13244         }
13245         
13246         // fixme ... this may be depricated need to test..
13247         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13248             
13249             var feedback = this.el.select('.form-control-feedback', true).first();
13250             
13251             if(feedback){
13252                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13253                 
13254                 if(this.getValue().length || this.forceFeedback){
13255                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13256                 }
13257                 
13258             }
13259             
13260         }
13261         
13262         this.fireEvent('invalid', this, msg);
13263     }
13264 });
13265
13266  
13267 /*
13268  * - LGPL
13269  *
13270  * trigger field - base class for combo..
13271  * 
13272  */
13273  
13274 /**
13275  * @class Roo.bootstrap.TriggerField
13276  * @extends Roo.bootstrap.Input
13277  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13278  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13279  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13280  * for which you can provide a custom implementation.  For example:
13281  * <pre><code>
13282 var trigger = new Roo.bootstrap.TriggerField();
13283 trigger.onTriggerClick = myTriggerFn;
13284 trigger.applyTo('my-field');
13285 </code></pre>
13286  *
13287  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13288  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13289  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13290  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13291  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13292
13293  * @constructor
13294  * Create a new TriggerField.
13295  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13296  * to the base TextField)
13297  */
13298 Roo.bootstrap.TriggerField = function(config){
13299     this.mimicing = false;
13300     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13301 };
13302
13303 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13304     /**
13305      * @cfg {String} triggerClass A CSS class to apply to the trigger
13306      */
13307      /**
13308      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13309      */
13310     hideTrigger:false,
13311
13312     /**
13313      * @cfg {Boolean} removable (true|false) special filter default false
13314      */
13315     removable : false,
13316     
13317     /** @cfg {Boolean} grow @hide */
13318     /** @cfg {Number} growMin @hide */
13319     /** @cfg {Number} growMax @hide */
13320
13321     /**
13322      * @hide 
13323      * @method
13324      */
13325     autoSize: Roo.emptyFn,
13326     // private
13327     monitorTab : true,
13328     // private
13329     deferHeight : true,
13330
13331     
13332     actionMode : 'wrap',
13333     
13334     caret : false,
13335     
13336     
13337     getAutoCreate : function(){
13338        
13339         var align = this.labelAlign || this.parentLabelAlign();
13340         
13341         var id = Roo.id();
13342         
13343         var cfg = {
13344             cls: 'form-group' //input-group
13345         };
13346         
13347         
13348         var input =  {
13349             tag: 'input',
13350             id : id,
13351             type : this.inputType,
13352             cls : 'form-control',
13353             autocomplete: 'new-password',
13354             placeholder : this.placeholder || '' 
13355             
13356         };
13357         if (this.name) {
13358             input.name = this.name;
13359         }
13360         if (this.size) {
13361             input.cls += ' input-' + this.size;
13362         }
13363         
13364         if (this.disabled) {
13365             input.disabled=true;
13366         }
13367         
13368         var inputblock = input;
13369         
13370         if(this.hasFeedback && !this.allowBlank){
13371             
13372             var feedback = {
13373                 tag: 'span',
13374                 cls: 'glyphicon form-control-feedback'
13375             };
13376             
13377             if(this.removable && !this.editable  ){
13378                 inputblock = {
13379                     cls : 'has-feedback',
13380                     cn :  [
13381                         inputblock,
13382                         {
13383                             tag: 'button',
13384                             html : 'x',
13385                             cls : 'roo-combo-removable-btn close'
13386                         },
13387                         feedback
13388                     ] 
13389                 };
13390             } else {
13391                 inputblock = {
13392                     cls : 'has-feedback',
13393                     cn :  [
13394                         inputblock,
13395                         feedback
13396                     ] 
13397                 };
13398             }
13399
13400         } else {
13401             if(this.removable && !this.editable ){
13402                 inputblock = {
13403                     cls : 'roo-removable',
13404                     cn :  [
13405                         inputblock,
13406                         {
13407                             tag: 'button',
13408                             html : 'x',
13409                             cls : 'roo-combo-removable-btn close'
13410                         }
13411                     ] 
13412                 };
13413             }
13414         }
13415         
13416         if (this.before || this.after) {
13417             
13418             inputblock = {
13419                 cls : 'input-group',
13420                 cn :  [] 
13421             };
13422             if (this.before) {
13423                 inputblock.cn.push({
13424                     tag :'span',
13425                     cls : 'input-group-addon input-group-prepend input-group-text',
13426                     html : this.before
13427                 });
13428             }
13429             
13430             inputblock.cn.push(input);
13431             
13432             if(this.hasFeedback && !this.allowBlank){
13433                 inputblock.cls += ' has-feedback';
13434                 inputblock.cn.push(feedback);
13435             }
13436             
13437             if (this.after) {
13438                 inputblock.cn.push({
13439                     tag :'span',
13440                     cls : 'input-group-addon input-group-append input-group-text',
13441                     html : this.after
13442                 });
13443             }
13444             
13445         };
13446         
13447       
13448         
13449         var ibwrap = inputblock;
13450         
13451         if(this.multiple){
13452             ibwrap = {
13453                 tag: 'ul',
13454                 cls: 'roo-select2-choices',
13455                 cn:[
13456                     {
13457                         tag: 'li',
13458                         cls: 'roo-select2-search-field',
13459                         cn: [
13460
13461                             inputblock
13462                         ]
13463                     }
13464                 ]
13465             };
13466                 
13467         }
13468         
13469         var combobox = {
13470             cls: 'roo-select2-container input-group',
13471             cn: [
13472                  {
13473                     tag: 'input',
13474                     type : 'hidden',
13475                     cls: 'form-hidden-field'
13476                 },
13477                 ibwrap
13478             ]
13479         };
13480         
13481         if(!this.multiple && this.showToggleBtn){
13482             
13483             var caret = {
13484                         tag: 'span',
13485                         cls: 'caret'
13486              };
13487             if (this.caret != false) {
13488                 caret = {
13489                      tag: 'i',
13490                      cls: 'fa fa-' + this.caret
13491                 };
13492                 
13493             }
13494             
13495             combobox.cn.push({
13496                 tag :'span',
13497                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13498                 cn : [
13499                     Roo.bootstrap.version == 3 ? caret : '',
13500                     {
13501                         tag: 'span',
13502                         cls: 'combobox-clear',
13503                         cn  : [
13504                             {
13505                                 tag : 'i',
13506                                 cls: 'icon-remove'
13507                             }
13508                         ]
13509                     }
13510                 ]
13511
13512             })
13513         }
13514         
13515         if(this.multiple){
13516             combobox.cls += ' roo-select2-container-multi';
13517         }
13518          var indicator = {
13519             tag : 'i',
13520             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13521             tooltip : 'This field is required'
13522         };
13523         if (Roo.bootstrap.version == 4) {
13524             indicator = {
13525                 tag : 'i',
13526                 style : 'display:none'
13527             };
13528         }
13529         
13530         
13531         if (align ==='left' && this.fieldLabel.length) {
13532             
13533             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13534
13535             cfg.cn = [
13536                 indicator,
13537                 {
13538                     tag: 'label',
13539                     'for' :  id,
13540                     cls : 'control-label',
13541                     html : this.fieldLabel
13542
13543                 },
13544                 {
13545                     cls : "", 
13546                     cn: [
13547                         combobox
13548                     ]
13549                 }
13550
13551             ];
13552             
13553             var labelCfg = cfg.cn[1];
13554             var contentCfg = cfg.cn[2];
13555             
13556             if(this.indicatorpos == 'right'){
13557                 cfg.cn = [
13558                     {
13559                         tag: 'label',
13560                         'for' :  id,
13561                         cls : 'control-label',
13562                         cn : [
13563                             {
13564                                 tag : 'span',
13565                                 html : this.fieldLabel
13566                             },
13567                             indicator
13568                         ]
13569                     },
13570                     {
13571                         cls : "", 
13572                         cn: [
13573                             combobox
13574                         ]
13575                     }
13576
13577                 ];
13578                 
13579                 labelCfg = cfg.cn[0];
13580                 contentCfg = cfg.cn[1];
13581             }
13582             
13583             if(this.labelWidth > 12){
13584                 labelCfg.style = "width: " + this.labelWidth + 'px';
13585             }
13586             
13587             if(this.labelWidth < 13 && this.labelmd == 0){
13588                 this.labelmd = this.labelWidth;
13589             }
13590             
13591             if(this.labellg > 0){
13592                 labelCfg.cls += ' col-lg-' + this.labellg;
13593                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13594             }
13595             
13596             if(this.labelmd > 0){
13597                 labelCfg.cls += ' col-md-' + this.labelmd;
13598                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13599             }
13600             
13601             if(this.labelsm > 0){
13602                 labelCfg.cls += ' col-sm-' + this.labelsm;
13603                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13604             }
13605             
13606             if(this.labelxs > 0){
13607                 labelCfg.cls += ' col-xs-' + this.labelxs;
13608                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13609             }
13610             
13611         } else if ( this.fieldLabel.length) {
13612 //                Roo.log(" label");
13613             cfg.cn = [
13614                 indicator,
13615                {
13616                    tag: 'label',
13617                    //cls : 'input-group-addon',
13618                    html : this.fieldLabel
13619
13620                },
13621
13622                combobox
13623
13624             ];
13625             
13626             if(this.indicatorpos == 'right'){
13627                 
13628                 cfg.cn = [
13629                     {
13630                        tag: 'label',
13631                        cn : [
13632                            {
13633                                tag : 'span',
13634                                html : this.fieldLabel
13635                            },
13636                            indicator
13637                        ]
13638
13639                     },
13640                     combobox
13641
13642                 ];
13643
13644             }
13645
13646         } else {
13647             
13648 //                Roo.log(" no label && no align");
13649                 cfg = combobox
13650                      
13651                 
13652         }
13653         
13654         var settings=this;
13655         ['xs','sm','md','lg'].map(function(size){
13656             if (settings[size]) {
13657                 cfg.cls += ' col-' + size + '-' + settings[size];
13658             }
13659         });
13660         
13661         return cfg;
13662         
13663     },
13664     
13665     
13666     
13667     // private
13668     onResize : function(w, h){
13669 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13670 //        if(typeof w == 'number'){
13671 //            var x = w - this.trigger.getWidth();
13672 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13673 //            this.trigger.setStyle('left', x+'px');
13674 //        }
13675     },
13676
13677     // private
13678     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13679
13680     // private
13681     getResizeEl : function(){
13682         return this.inputEl();
13683     },
13684
13685     // private
13686     getPositionEl : function(){
13687         return this.inputEl();
13688     },
13689
13690     // private
13691     alignErrorIcon : function(){
13692         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13693     },
13694
13695     // private
13696     initEvents : function(){
13697         
13698         this.createList();
13699         
13700         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13701         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13702         if(!this.multiple && this.showToggleBtn){
13703             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13704             if(this.hideTrigger){
13705                 this.trigger.setDisplayed(false);
13706             }
13707             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13708         }
13709         
13710         if(this.multiple){
13711             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13712         }
13713         
13714         if(this.removable && !this.editable && !this.tickable){
13715             var close = this.closeTriggerEl();
13716             
13717             if(close){
13718                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13719                 close.on('click', this.removeBtnClick, this, close);
13720             }
13721         }
13722         
13723         //this.trigger.addClassOnOver('x-form-trigger-over');
13724         //this.trigger.addClassOnClick('x-form-trigger-click');
13725         
13726         //if(!this.width){
13727         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13728         //}
13729     },
13730     
13731     closeTriggerEl : function()
13732     {
13733         var close = this.el.select('.roo-combo-removable-btn', true).first();
13734         return close ? close : false;
13735     },
13736     
13737     removeBtnClick : function(e, h, el)
13738     {
13739         e.preventDefault();
13740         
13741         if(this.fireEvent("remove", this) !== false){
13742             this.reset();
13743             this.fireEvent("afterremove", this)
13744         }
13745     },
13746     
13747     createList : function()
13748     {
13749         this.list = Roo.get(document.body).createChild({
13750             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13751             cls: 'typeahead typeahead-long dropdown-menu shadow',
13752             style: 'display:none'
13753         });
13754         
13755         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13756         
13757     },
13758
13759     // private
13760     initTrigger : function(){
13761        
13762     },
13763
13764     // private
13765     onDestroy : function(){
13766         if(this.trigger){
13767             this.trigger.removeAllListeners();
13768           //  this.trigger.remove();
13769         }
13770         //if(this.wrap){
13771         //    this.wrap.remove();
13772         //}
13773         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13774     },
13775
13776     // private
13777     onFocus : function(){
13778         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13779         /*
13780         if(!this.mimicing){
13781             this.wrap.addClass('x-trigger-wrap-focus');
13782             this.mimicing = true;
13783             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13784             if(this.monitorTab){
13785                 this.el.on("keydown", this.checkTab, this);
13786             }
13787         }
13788         */
13789     },
13790
13791     // private
13792     checkTab : function(e){
13793         if(e.getKey() == e.TAB){
13794             this.triggerBlur();
13795         }
13796     },
13797
13798     // private
13799     onBlur : function(){
13800         // do nothing
13801     },
13802
13803     // private
13804     mimicBlur : function(e, t){
13805         /*
13806         if(!this.wrap.contains(t) && this.validateBlur()){
13807             this.triggerBlur();
13808         }
13809         */
13810     },
13811
13812     // private
13813     triggerBlur : function(){
13814         this.mimicing = false;
13815         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13816         if(this.monitorTab){
13817             this.el.un("keydown", this.checkTab, this);
13818         }
13819         //this.wrap.removeClass('x-trigger-wrap-focus');
13820         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13821     },
13822
13823     // private
13824     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13825     validateBlur : function(e, t){
13826         return true;
13827     },
13828
13829     // private
13830     onDisable : function(){
13831         this.inputEl().dom.disabled = true;
13832         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13833         //if(this.wrap){
13834         //    this.wrap.addClass('x-item-disabled');
13835         //}
13836     },
13837
13838     // private
13839     onEnable : function(){
13840         this.inputEl().dom.disabled = false;
13841         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13842         //if(this.wrap){
13843         //    this.el.removeClass('x-item-disabled');
13844         //}
13845     },
13846
13847     // private
13848     onShow : function(){
13849         var ae = this.getActionEl();
13850         
13851         if(ae){
13852             ae.dom.style.display = '';
13853             ae.dom.style.visibility = 'visible';
13854         }
13855     },
13856
13857     // private
13858     
13859     onHide : function(){
13860         var ae = this.getActionEl();
13861         ae.dom.style.display = 'none';
13862     },
13863
13864     /**
13865      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13866      * by an implementing function.
13867      * @method
13868      * @param {EventObject} e
13869      */
13870     onTriggerClick : Roo.emptyFn
13871 });
13872  
13873 /*
13874 * Licence: LGPL
13875 */
13876
13877 /**
13878  * @class Roo.bootstrap.CardUploader
13879  * @extends Roo.bootstrap.Button
13880  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13881  * @cfg {Number} errorTimeout default 3000
13882  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13883  * @cfg {Array}  html The button text.
13884
13885  *
13886  * @constructor
13887  * Create a new CardUploader
13888  * @param {Object} config The config object
13889  */
13890
13891 Roo.bootstrap.CardUploader = function(config){
13892     
13893  
13894     
13895     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13896     
13897     
13898     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13899         return r.data.id
13900      });
13901     
13902      this.addEvents({
13903          // raw events
13904         /**
13905          * @event preview
13906          * When a image is clicked on - and needs to display a slideshow or similar..
13907          * @param {Roo.bootstrap.Card} this
13908          * @param {Object} The image information data 
13909          *
13910          */
13911         'preview' : true,
13912          /**
13913          * @event download
13914          * When a the download link is clicked
13915          * @param {Roo.bootstrap.Card} this
13916          * @param {Object} The image information data  contains 
13917          */
13918         'download' : true
13919         
13920     });
13921 };
13922  
13923 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13924     
13925      
13926     errorTimeout : 3000,
13927      
13928     images : false,
13929    
13930     fileCollection : false,
13931     allowBlank : true,
13932     
13933     getAutoCreate : function()
13934     {
13935         
13936         var cfg =  {
13937             cls :'form-group' ,
13938             cn : [
13939                
13940                 {
13941                     tag: 'label',
13942                    //cls : 'input-group-addon',
13943                     html : this.fieldLabel
13944
13945                 },
13946
13947                 {
13948                     tag: 'input',
13949                     type : 'hidden',
13950                     name : this.name,
13951                     value : this.value,
13952                     cls : 'd-none  form-control'
13953                 },
13954                 
13955                 {
13956                     tag: 'input',
13957                     multiple : 'multiple',
13958                     type : 'file',
13959                     cls : 'd-none  roo-card-upload-selector'
13960                 },
13961                 
13962                 {
13963                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13964                 },
13965                 {
13966                     cls : 'card-columns roo-card-uploader-container'
13967                 }
13968
13969             ]
13970         };
13971            
13972          
13973         return cfg;
13974     },
13975     
13976     getChildContainer : function() /// what children are added to.
13977     {
13978         return this.containerEl;
13979     },
13980    
13981     getButtonContainer : function() /// what children are added to.
13982     {
13983         return this.el.select(".roo-card-uploader-button-container").first();
13984     },
13985    
13986     initEvents : function()
13987     {
13988         
13989         Roo.bootstrap.Input.prototype.initEvents.call(this);
13990         
13991         var t = this;
13992         this.addxtype({
13993             xns: Roo.bootstrap,
13994
13995             xtype : 'Button',
13996             container_method : 'getButtonContainer' ,            
13997             html :  this.html, // fix changable?
13998             cls : 'w-100 ',
13999             listeners : {
14000                 'click' : function(btn, e) {
14001                     t.onClick(e);
14002                 }
14003             }
14004         });
14005         
14006         
14007         
14008         
14009         this.urlAPI = (window.createObjectURL && window) || 
14010                                 (window.URL && URL.revokeObjectURL && URL) || 
14011                                 (window.webkitURL && webkitURL);
14012                         
14013          
14014          
14015          
14016         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14017         
14018         this.selectorEl.on('change', this.onFileSelected, this);
14019         if (this.images) {
14020             var t = this;
14021             this.images.forEach(function(img) {
14022                 t.addCard(img)
14023             });
14024             this.images = false;
14025         }
14026         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14027          
14028        
14029     },
14030     
14031    
14032     onClick : function(e)
14033     {
14034         e.preventDefault();
14035          
14036         this.selectorEl.dom.click();
14037          
14038     },
14039     
14040     onFileSelected : function(e)
14041     {
14042         e.preventDefault();
14043         
14044         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14045             return;
14046         }
14047         
14048         Roo.each(this.selectorEl.dom.files, function(file){    
14049             this.addFile(file);
14050         }, this);
14051          
14052     },
14053     
14054       
14055     
14056       
14057     
14058     addFile : function(file)
14059     {
14060            
14061         if(typeof(file) === 'string'){
14062             throw "Add file by name?"; // should not happen
14063             return;
14064         }
14065         
14066         if(!file || !this.urlAPI){
14067             return;
14068         }
14069         
14070         // file;
14071         // file.type;
14072         
14073         var _this = this;
14074         
14075         
14076         var url = _this.urlAPI.createObjectURL( file);
14077            
14078         this.addCard({
14079             id : Roo.bootstrap.CardUploader.ID--,
14080             is_uploaded : false,
14081             src : url,
14082             srcfile : file,
14083             title : file.name,
14084             mimetype : file.type,
14085             preview : false,
14086             is_deleted : 0
14087         });
14088         
14089     },
14090     
14091     /**
14092      * addCard - add an Attachment to the uploader
14093      * @param data - the data about the image to upload
14094      *
14095      * {
14096           id : 123
14097           title : "Title of file",
14098           is_uploaded : false,
14099           src : "http://.....",
14100           srcfile : { the File upload object },
14101           mimetype : file.type,
14102           preview : false,
14103           is_deleted : 0
14104           .. any other data...
14105         }
14106      *
14107      * 
14108     */
14109     
14110     addCard : function (data)
14111     {
14112         // hidden input element?
14113         // if the file is not an image...
14114         //then we need to use something other that and header_image
14115         var t = this;
14116         //   remove.....
14117         var footer = [
14118             {
14119                 xns : Roo.bootstrap,
14120                 xtype : 'CardFooter',
14121                  items: [
14122                     {
14123                         xns : Roo.bootstrap,
14124                         xtype : 'Element',
14125                         cls : 'd-flex',
14126                         items : [
14127                             
14128                             {
14129                                 xns : Roo.bootstrap,
14130                                 xtype : 'Button',
14131                                 html : String.format("<small>{0}</small>", data.title),
14132                                 cls : 'col-10 text-left',
14133                                 size: 'sm',
14134                                 weight: 'link',
14135                                 fa : 'download',
14136                                 listeners : {
14137                                     click : function() {
14138                                      
14139                                         t.fireEvent( "download", t, data );
14140                                     }
14141                                 }
14142                             },
14143                           
14144                             {
14145                                 xns : Roo.bootstrap,
14146                                 xtype : 'Button',
14147                                 style: 'max-height: 28px; ',
14148                                 size : 'sm',
14149                                 weight: 'danger',
14150                                 cls : 'col-2',
14151                                 fa : 'times',
14152                                 listeners : {
14153                                     click : function() {
14154                                         t.removeCard(data.id)
14155                                     }
14156                                 }
14157                             }
14158                         ]
14159                     }
14160                     
14161                 ] 
14162             }
14163             
14164         ];
14165         
14166         var cn = this.addxtype(
14167             {
14168                  
14169                 xns : Roo.bootstrap,
14170                 xtype : 'Card',
14171                 closeable : true,
14172                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14173                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14174                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14175                 data : data,
14176                 html : false,
14177                  
14178                 items : footer,
14179                 initEvents : function() {
14180                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14181                     var card = this;
14182                     this.imgEl = this.el.select('.card-img-top').first();
14183                     if (this.imgEl) {
14184                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14185                         this.imgEl.set({ 'pointer' : 'cursor' });
14186                                   
14187                     }
14188                     this.getCardFooter().addClass('p-1');
14189                     
14190                   
14191                 }
14192                 
14193             }
14194         );
14195         // dont' really need ot update items.
14196         // this.items.push(cn);
14197         this.fileCollection.add(cn);
14198         
14199         if (!data.srcfile) {
14200             this.updateInput();
14201             return;
14202         }
14203             
14204         var _t = this;
14205         var reader = new FileReader();
14206         reader.addEventListener("load", function() {  
14207             data.srcdata =  reader.result;
14208             _t.updateInput();
14209         });
14210         reader.readAsDataURL(data.srcfile);
14211         
14212         
14213         
14214     },
14215     removeCard : function(id)
14216     {
14217         
14218         var card  = this.fileCollection.get(id);
14219         card.data.is_deleted = 1;
14220         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14221         //this.fileCollection.remove(card);
14222         //this.items = this.items.filter(function(e) { return e != card });
14223         // dont' really need ot update items.
14224         card.el.dom.parentNode.removeChild(card.el.dom);
14225         this.updateInput();
14226
14227         
14228     },
14229     reset: function()
14230     {
14231         this.fileCollection.each(function(card) {
14232             if (card.el.dom && card.el.dom.parentNode) {
14233                 card.el.dom.parentNode.removeChild(card.el.dom);
14234             }
14235         });
14236         this.fileCollection.clear();
14237         this.updateInput();
14238     },
14239     
14240     updateInput : function()
14241     {
14242          var data = [];
14243         this.fileCollection.each(function(e) {
14244             data.push(e.data);
14245             
14246         });
14247         this.inputEl().dom.value = JSON.stringify(data);
14248         
14249         
14250         
14251     }
14252     
14253     
14254 });
14255
14256
14257 Roo.bootstrap.CardUploader.ID = -1;/*
14258  * Based on:
14259  * Ext JS Library 1.1.1
14260  * Copyright(c) 2006-2007, Ext JS, LLC.
14261  *
14262  * Originally Released Under LGPL - original licence link has changed is not relivant.
14263  *
14264  * Fork - LGPL
14265  * <script type="text/javascript">
14266  */
14267
14268
14269 /**
14270  * @class Roo.data.SortTypes
14271  * @singleton
14272  * Defines the default sorting (casting?) comparison functions used when sorting data.
14273  */
14274 Roo.data.SortTypes = {
14275     /**
14276      * Default sort that does nothing
14277      * @param {Mixed} s The value being converted
14278      * @return {Mixed} The comparison value
14279      */
14280     none : function(s){
14281         return s;
14282     },
14283     
14284     /**
14285      * The regular expression used to strip tags
14286      * @type {RegExp}
14287      * @property
14288      */
14289     stripTagsRE : /<\/?[^>]+>/gi,
14290     
14291     /**
14292      * Strips all HTML tags to sort on text only
14293      * @param {Mixed} s The value being converted
14294      * @return {String} The comparison value
14295      */
14296     asText : function(s){
14297         return String(s).replace(this.stripTagsRE, "");
14298     },
14299     
14300     /**
14301      * Strips all HTML tags to sort on text only - Case insensitive
14302      * @param {Mixed} s The value being converted
14303      * @return {String} The comparison value
14304      */
14305     asUCText : function(s){
14306         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14307     },
14308     
14309     /**
14310      * Case insensitive string
14311      * @param {Mixed} s The value being converted
14312      * @return {String} The comparison value
14313      */
14314     asUCString : function(s) {
14315         return String(s).toUpperCase();
14316     },
14317     
14318     /**
14319      * Date sorting
14320      * @param {Mixed} s The value being converted
14321      * @return {Number} The comparison value
14322      */
14323     asDate : function(s) {
14324         if(!s){
14325             return 0;
14326         }
14327         if(s instanceof Date){
14328             return s.getTime();
14329         }
14330         return Date.parse(String(s));
14331     },
14332     
14333     /**
14334      * Float sorting
14335      * @param {Mixed} s The value being converted
14336      * @return {Float} The comparison value
14337      */
14338     asFloat : function(s) {
14339         var val = parseFloat(String(s).replace(/,/g, ""));
14340         if(isNaN(val)) {
14341             val = 0;
14342         }
14343         return val;
14344     },
14345     
14346     /**
14347      * Integer sorting
14348      * @param {Mixed} s The value being converted
14349      * @return {Number} The comparison value
14350      */
14351     asInt : function(s) {
14352         var val = parseInt(String(s).replace(/,/g, ""));
14353         if(isNaN(val)) {
14354             val = 0;
14355         }
14356         return val;
14357     }
14358 };/*
14359  * Based on:
14360  * Ext JS Library 1.1.1
14361  * Copyright(c) 2006-2007, Ext JS, LLC.
14362  *
14363  * Originally Released Under LGPL - original licence link has changed is not relivant.
14364  *
14365  * Fork - LGPL
14366  * <script type="text/javascript">
14367  */
14368
14369 /**
14370 * @class Roo.data.Record
14371  * Instances of this class encapsulate both record <em>definition</em> information, and record
14372  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14373  * to access Records cached in an {@link Roo.data.Store} object.<br>
14374  * <p>
14375  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14376  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14377  * objects.<br>
14378  * <p>
14379  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14380  * @constructor
14381  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14382  * {@link #create}. The parameters are the same.
14383  * @param {Array} data An associative Array of data values keyed by the field name.
14384  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14385  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14386  * not specified an integer id is generated.
14387  */
14388 Roo.data.Record = function(data, id){
14389     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14390     this.data = data;
14391 };
14392
14393 /**
14394  * Generate a constructor for a specific record layout.
14395  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14396  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14397  * Each field definition object may contain the following properties: <ul>
14398  * <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,
14399  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14400  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14401  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14402  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14403  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14404  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14405  * this may be omitted.</p></li>
14406  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14407  * <ul><li>auto (Default, implies no conversion)</li>
14408  * <li>string</li>
14409  * <li>int</li>
14410  * <li>float</li>
14411  * <li>boolean</li>
14412  * <li>date</li></ul></p></li>
14413  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14414  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14415  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14416  * by the Reader into an object that will be stored in the Record. It is passed the
14417  * following parameters:<ul>
14418  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14419  * </ul></p></li>
14420  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14421  * </ul>
14422  * <br>usage:<br><pre><code>
14423 var TopicRecord = Roo.data.Record.create(
14424     {name: 'title', mapping: 'topic_title'},
14425     {name: 'author', mapping: 'username'},
14426     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14427     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14428     {name: 'lastPoster', mapping: 'user2'},
14429     {name: 'excerpt', mapping: 'post_text'}
14430 );
14431
14432 var myNewRecord = new TopicRecord({
14433     title: 'Do my job please',
14434     author: 'noobie',
14435     totalPosts: 1,
14436     lastPost: new Date(),
14437     lastPoster: 'Animal',
14438     excerpt: 'No way dude!'
14439 });
14440 myStore.add(myNewRecord);
14441 </code></pre>
14442  * @method create
14443  * @static
14444  */
14445 Roo.data.Record.create = function(o){
14446     var f = function(){
14447         f.superclass.constructor.apply(this, arguments);
14448     };
14449     Roo.extend(f, Roo.data.Record);
14450     var p = f.prototype;
14451     p.fields = new Roo.util.MixedCollection(false, function(field){
14452         return field.name;
14453     });
14454     for(var i = 0, len = o.length; i < len; i++){
14455         p.fields.add(new Roo.data.Field(o[i]));
14456     }
14457     f.getField = function(name){
14458         return p.fields.get(name);  
14459     };
14460     return f;
14461 };
14462
14463 Roo.data.Record.AUTO_ID = 1000;
14464 Roo.data.Record.EDIT = 'edit';
14465 Roo.data.Record.REJECT = 'reject';
14466 Roo.data.Record.COMMIT = 'commit';
14467
14468 Roo.data.Record.prototype = {
14469     /**
14470      * Readonly flag - true if this record has been modified.
14471      * @type Boolean
14472      */
14473     dirty : false,
14474     editing : false,
14475     error: null,
14476     modified: null,
14477
14478     // private
14479     join : function(store){
14480         this.store = store;
14481     },
14482
14483     /**
14484      * Set the named field to the specified value.
14485      * @param {String} name The name of the field to set.
14486      * @param {Object} value The value to set the field to.
14487      */
14488     set : function(name, value){
14489         if(this.data[name] == value){
14490             return;
14491         }
14492         this.dirty = true;
14493         if(!this.modified){
14494             this.modified = {};
14495         }
14496         if(typeof this.modified[name] == 'undefined'){
14497             this.modified[name] = this.data[name];
14498         }
14499         this.data[name] = value;
14500         if(!this.editing && this.store){
14501             this.store.afterEdit(this);
14502         }       
14503     },
14504
14505     /**
14506      * Get the value of the named field.
14507      * @param {String} name The name of the field to get the value of.
14508      * @return {Object} The value of the field.
14509      */
14510     get : function(name){
14511         return this.data[name]; 
14512     },
14513
14514     // private
14515     beginEdit : function(){
14516         this.editing = true;
14517         this.modified = {}; 
14518     },
14519
14520     // private
14521     cancelEdit : function(){
14522         this.editing = false;
14523         delete this.modified;
14524     },
14525
14526     // private
14527     endEdit : function(){
14528         this.editing = false;
14529         if(this.dirty && this.store){
14530             this.store.afterEdit(this);
14531         }
14532     },
14533
14534     /**
14535      * Usually called by the {@link Roo.data.Store} which owns the Record.
14536      * Rejects all changes made to the Record since either creation, or the last commit operation.
14537      * Modified fields are reverted to their original values.
14538      * <p>
14539      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14540      * of reject operations.
14541      */
14542     reject : function(){
14543         var m = this.modified;
14544         for(var n in m){
14545             if(typeof m[n] != "function"){
14546                 this.data[n] = m[n];
14547             }
14548         }
14549         this.dirty = false;
14550         delete this.modified;
14551         this.editing = false;
14552         if(this.store){
14553             this.store.afterReject(this);
14554         }
14555     },
14556
14557     /**
14558      * Usually called by the {@link Roo.data.Store} which owns the Record.
14559      * Commits all changes made to the Record since either creation, or the last commit operation.
14560      * <p>
14561      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14562      * of commit operations.
14563      */
14564     commit : function(){
14565         this.dirty = false;
14566         delete this.modified;
14567         this.editing = false;
14568         if(this.store){
14569             this.store.afterCommit(this);
14570         }
14571     },
14572
14573     // private
14574     hasError : function(){
14575         return this.error != null;
14576     },
14577
14578     // private
14579     clearError : function(){
14580         this.error = null;
14581     },
14582
14583     /**
14584      * Creates a copy of this record.
14585      * @param {String} id (optional) A new record id if you don't want to use this record's id
14586      * @return {Record}
14587      */
14588     copy : function(newId) {
14589         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14590     }
14591 };/*
14592  * Based on:
14593  * Ext JS Library 1.1.1
14594  * Copyright(c) 2006-2007, Ext JS, LLC.
14595  *
14596  * Originally Released Under LGPL - original licence link has changed is not relivant.
14597  *
14598  * Fork - LGPL
14599  * <script type="text/javascript">
14600  */
14601
14602
14603
14604 /**
14605  * @class Roo.data.Store
14606  * @extends Roo.util.Observable
14607  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14608  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14609  * <p>
14610  * 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
14611  * has no knowledge of the format of the data returned by the Proxy.<br>
14612  * <p>
14613  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14614  * instances from the data object. These records are cached and made available through accessor functions.
14615  * @constructor
14616  * Creates a new Store.
14617  * @param {Object} config A config object containing the objects needed for the Store to access data,
14618  * and read the data into Records.
14619  */
14620 Roo.data.Store = function(config){
14621     this.data = new Roo.util.MixedCollection(false);
14622     this.data.getKey = function(o){
14623         return o.id;
14624     };
14625     this.baseParams = {};
14626     // private
14627     this.paramNames = {
14628         "start" : "start",
14629         "limit" : "limit",
14630         "sort" : "sort",
14631         "dir" : "dir",
14632         "multisort" : "_multisort"
14633     };
14634
14635     if(config && config.data){
14636         this.inlineData = config.data;
14637         delete config.data;
14638     }
14639
14640     Roo.apply(this, config);
14641     
14642     if(this.reader){ // reader passed
14643         this.reader = Roo.factory(this.reader, Roo.data);
14644         this.reader.xmodule = this.xmodule || false;
14645         if(!this.recordType){
14646             this.recordType = this.reader.recordType;
14647         }
14648         if(this.reader.onMetaChange){
14649             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14650         }
14651     }
14652
14653     if(this.recordType){
14654         this.fields = this.recordType.prototype.fields;
14655     }
14656     this.modified = [];
14657
14658     this.addEvents({
14659         /**
14660          * @event datachanged
14661          * Fires when the data cache has changed, and a widget which is using this Store
14662          * as a Record cache should refresh its view.
14663          * @param {Store} this
14664          */
14665         datachanged : true,
14666         /**
14667          * @event metachange
14668          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14669          * @param {Store} this
14670          * @param {Object} meta The JSON metadata
14671          */
14672         metachange : true,
14673         /**
14674          * @event add
14675          * Fires when Records have been added to the Store
14676          * @param {Store} this
14677          * @param {Roo.data.Record[]} records The array of Records added
14678          * @param {Number} index The index at which the record(s) were added
14679          */
14680         add : true,
14681         /**
14682          * @event remove
14683          * Fires when a Record has been removed from the Store
14684          * @param {Store} this
14685          * @param {Roo.data.Record} record The Record that was removed
14686          * @param {Number} index The index at which the record was removed
14687          */
14688         remove : true,
14689         /**
14690          * @event update
14691          * Fires when a Record has been updated
14692          * @param {Store} this
14693          * @param {Roo.data.Record} record The Record that was updated
14694          * @param {String} operation The update operation being performed.  Value may be one of:
14695          * <pre><code>
14696  Roo.data.Record.EDIT
14697  Roo.data.Record.REJECT
14698  Roo.data.Record.COMMIT
14699          * </code></pre>
14700          */
14701         update : true,
14702         /**
14703          * @event clear
14704          * Fires when the data cache has been cleared.
14705          * @param {Store} this
14706          */
14707         clear : true,
14708         /**
14709          * @event beforeload
14710          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14711          * the load action will be canceled.
14712          * @param {Store} this
14713          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14714          */
14715         beforeload : true,
14716         /**
14717          * @event beforeloadadd
14718          * Fires after a new set of Records has been loaded.
14719          * @param {Store} this
14720          * @param {Roo.data.Record[]} records The Records that were loaded
14721          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14722          */
14723         beforeloadadd : true,
14724         /**
14725          * @event load
14726          * Fires after a new set of Records has been loaded, before they are added to the store.
14727          * @param {Store} this
14728          * @param {Roo.data.Record[]} records The Records that were loaded
14729          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14730          * @params {Object} return from reader
14731          */
14732         load : true,
14733         /**
14734          * @event loadexception
14735          * Fires if an exception occurs in the Proxy during loading.
14736          * Called with the signature of the Proxy's "loadexception" event.
14737          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14738          * 
14739          * @param {Proxy} 
14740          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14741          * @param {Object} load options 
14742          * @param {Object} jsonData from your request (normally this contains the Exception)
14743          */
14744         loadexception : true
14745     });
14746     
14747     if(this.proxy){
14748         this.proxy = Roo.factory(this.proxy, Roo.data);
14749         this.proxy.xmodule = this.xmodule || false;
14750         this.relayEvents(this.proxy,  ["loadexception"]);
14751     }
14752     this.sortToggle = {};
14753     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14754
14755     Roo.data.Store.superclass.constructor.call(this);
14756
14757     if(this.inlineData){
14758         this.loadData(this.inlineData);
14759         delete this.inlineData;
14760     }
14761 };
14762
14763 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14764      /**
14765     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14766     * without a remote query - used by combo/forms at present.
14767     */
14768     
14769     /**
14770     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14771     */
14772     /**
14773     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14774     */
14775     /**
14776     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14777     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14778     */
14779     /**
14780     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14781     * on any HTTP request
14782     */
14783     /**
14784     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14785     */
14786     /**
14787     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14788     */
14789     multiSort: false,
14790     /**
14791     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14792     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14793     */
14794     remoteSort : false,
14795
14796     /**
14797     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14798      * loaded or when a record is removed. (defaults to false).
14799     */
14800     pruneModifiedRecords : false,
14801
14802     // private
14803     lastOptions : null,
14804
14805     /**
14806      * Add Records to the Store and fires the add event.
14807      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14808      */
14809     add : function(records){
14810         records = [].concat(records);
14811         for(var i = 0, len = records.length; i < len; i++){
14812             records[i].join(this);
14813         }
14814         var index = this.data.length;
14815         this.data.addAll(records);
14816         this.fireEvent("add", this, records, index);
14817     },
14818
14819     /**
14820      * Remove a Record from the Store and fires the remove event.
14821      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14822      */
14823     remove : function(record){
14824         var index = this.data.indexOf(record);
14825         this.data.removeAt(index);
14826  
14827         if(this.pruneModifiedRecords){
14828             this.modified.remove(record);
14829         }
14830         this.fireEvent("remove", this, record, index);
14831     },
14832
14833     /**
14834      * Remove all Records from the Store and fires the clear event.
14835      */
14836     removeAll : function(){
14837         this.data.clear();
14838         if(this.pruneModifiedRecords){
14839             this.modified = [];
14840         }
14841         this.fireEvent("clear", this);
14842     },
14843
14844     /**
14845      * Inserts Records to the Store at the given index and fires the add event.
14846      * @param {Number} index The start index at which to insert the passed Records.
14847      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14848      */
14849     insert : function(index, records){
14850         records = [].concat(records);
14851         for(var i = 0, len = records.length; i < len; i++){
14852             this.data.insert(index, records[i]);
14853             records[i].join(this);
14854         }
14855         this.fireEvent("add", this, records, index);
14856     },
14857
14858     /**
14859      * Get the index within the cache of the passed Record.
14860      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14861      * @return {Number} The index of the passed Record. Returns -1 if not found.
14862      */
14863     indexOf : function(record){
14864         return this.data.indexOf(record);
14865     },
14866
14867     /**
14868      * Get the index within the cache of the Record with the passed id.
14869      * @param {String} id The id of the Record to find.
14870      * @return {Number} The index of the Record. Returns -1 if not found.
14871      */
14872     indexOfId : function(id){
14873         return this.data.indexOfKey(id);
14874     },
14875
14876     /**
14877      * Get the Record with the specified id.
14878      * @param {String} id The id of the Record to find.
14879      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14880      */
14881     getById : function(id){
14882         return this.data.key(id);
14883     },
14884
14885     /**
14886      * Get the Record at the specified index.
14887      * @param {Number} index The index of the Record to find.
14888      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14889      */
14890     getAt : function(index){
14891         return this.data.itemAt(index);
14892     },
14893
14894     /**
14895      * Returns a range of Records between specified indices.
14896      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14897      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14898      * @return {Roo.data.Record[]} An array of Records
14899      */
14900     getRange : function(start, end){
14901         return this.data.getRange(start, end);
14902     },
14903
14904     // private
14905     storeOptions : function(o){
14906         o = Roo.apply({}, o);
14907         delete o.callback;
14908         delete o.scope;
14909         this.lastOptions = o;
14910     },
14911
14912     /**
14913      * Loads the Record cache from the configured Proxy using the configured Reader.
14914      * <p>
14915      * If using remote paging, then the first load call must specify the <em>start</em>
14916      * and <em>limit</em> properties in the options.params property to establish the initial
14917      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14918      * <p>
14919      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14920      * and this call will return before the new data has been loaded. Perform any post-processing
14921      * in a callback function, or in a "load" event handler.</strong>
14922      * <p>
14923      * @param {Object} options An object containing properties which control loading options:<ul>
14924      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14925      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14926      * passed the following arguments:<ul>
14927      * <li>r : Roo.data.Record[]</li>
14928      * <li>options: Options object from the load call</li>
14929      * <li>success: Boolean success indicator</li></ul></li>
14930      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14931      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14932      * </ul>
14933      */
14934     load : function(options){
14935         options = options || {};
14936         if(this.fireEvent("beforeload", this, options) !== false){
14937             this.storeOptions(options);
14938             var p = Roo.apply(options.params || {}, this.baseParams);
14939             // if meta was not loaded from remote source.. try requesting it.
14940             if (!this.reader.metaFromRemote) {
14941                 p._requestMeta = 1;
14942             }
14943             if(this.sortInfo && this.remoteSort){
14944                 var pn = this.paramNames;
14945                 p[pn["sort"]] = this.sortInfo.field;
14946                 p[pn["dir"]] = this.sortInfo.direction;
14947             }
14948             if (this.multiSort) {
14949                 var pn = this.paramNames;
14950                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14951             }
14952             
14953             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14954         }
14955     },
14956
14957     /**
14958      * Reloads the Record cache from the configured Proxy using the configured Reader and
14959      * the options from the last load operation performed.
14960      * @param {Object} options (optional) An object containing properties which may override the options
14961      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14962      * the most recently used options are reused).
14963      */
14964     reload : function(options){
14965         this.load(Roo.applyIf(options||{}, this.lastOptions));
14966     },
14967
14968     // private
14969     // Called as a callback by the Reader during a load operation.
14970     loadRecords : function(o, options, success){
14971         if(!o || success === false){
14972             if(success !== false){
14973                 this.fireEvent("load", this, [], options, o);
14974             }
14975             if(options.callback){
14976                 options.callback.call(options.scope || this, [], options, false);
14977             }
14978             return;
14979         }
14980         // if data returned failure - throw an exception.
14981         if (o.success === false) {
14982             // show a message if no listener is registered.
14983             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14984                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14985             }
14986             // loadmask wil be hooked into this..
14987             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14988             return;
14989         }
14990         var r = o.records, t = o.totalRecords || r.length;
14991         
14992         this.fireEvent("beforeloadadd", this, r, options, o);
14993         
14994         if(!options || options.add !== true){
14995             if(this.pruneModifiedRecords){
14996                 this.modified = [];
14997             }
14998             for(var i = 0, len = r.length; i < len; i++){
14999                 r[i].join(this);
15000             }
15001             if(this.snapshot){
15002                 this.data = this.snapshot;
15003                 delete this.snapshot;
15004             }
15005             this.data.clear();
15006             this.data.addAll(r);
15007             this.totalLength = t;
15008             this.applySort();
15009             this.fireEvent("datachanged", this);
15010         }else{
15011             this.totalLength = Math.max(t, this.data.length+r.length);
15012             this.add(r);
15013         }
15014         
15015         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15016                 
15017             var e = new Roo.data.Record({});
15018
15019             e.set(this.parent.displayField, this.parent.emptyTitle);
15020             e.set(this.parent.valueField, '');
15021
15022             this.insert(0, e);
15023         }
15024             
15025         this.fireEvent("load", this, r, options, o);
15026         if(options.callback){
15027             options.callback.call(options.scope || this, r, options, true);
15028         }
15029     },
15030
15031
15032     /**
15033      * Loads data from a passed data block. A Reader which understands the format of the data
15034      * must have been configured in the constructor.
15035      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15036      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15037      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15038      */
15039     loadData : function(o, append){
15040         var r = this.reader.readRecords(o);
15041         this.loadRecords(r, {add: append}, true);
15042     },
15043     
15044      /**
15045      * using 'cn' the nested child reader read the child array into it's child stores.
15046      * @param {Object} rec The record with a 'children array
15047      */
15048     loadDataFromChildren : function(rec)
15049     {
15050         this.loadData(this.reader.toLoadData(rec));
15051     },
15052     
15053
15054     /**
15055      * Gets the number of cached records.
15056      * <p>
15057      * <em>If using paging, this may not be the total size of the dataset. If the data object
15058      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15059      * the data set size</em>
15060      */
15061     getCount : function(){
15062         return this.data.length || 0;
15063     },
15064
15065     /**
15066      * Gets the total number of records in the dataset as returned by the server.
15067      * <p>
15068      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15069      * the dataset size</em>
15070      */
15071     getTotalCount : function(){
15072         return this.totalLength || 0;
15073     },
15074
15075     /**
15076      * Returns the sort state of the Store as an object with two properties:
15077      * <pre><code>
15078  field {String} The name of the field by which the Records are sorted
15079  direction {String} The sort order, "ASC" or "DESC"
15080      * </code></pre>
15081      */
15082     getSortState : function(){
15083         return this.sortInfo;
15084     },
15085
15086     // private
15087     applySort : function(){
15088         if(this.sortInfo && !this.remoteSort){
15089             var s = this.sortInfo, f = s.field;
15090             var st = this.fields.get(f).sortType;
15091             var fn = function(r1, r2){
15092                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15093                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15094             };
15095             this.data.sort(s.direction, fn);
15096             if(this.snapshot && this.snapshot != this.data){
15097                 this.snapshot.sort(s.direction, fn);
15098             }
15099         }
15100     },
15101
15102     /**
15103      * Sets the default sort column and order to be used by the next load operation.
15104      * @param {String} fieldName The name of the field to sort by.
15105      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15106      */
15107     setDefaultSort : function(field, dir){
15108         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15109     },
15110
15111     /**
15112      * Sort the Records.
15113      * If remote sorting is used, the sort is performed on the server, and the cache is
15114      * reloaded. If local sorting is used, the cache is sorted internally.
15115      * @param {String} fieldName The name of the field to sort by.
15116      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15117      */
15118     sort : function(fieldName, dir){
15119         var f = this.fields.get(fieldName);
15120         if(!dir){
15121             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15122             
15123             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15124                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15125             }else{
15126                 dir = f.sortDir;
15127             }
15128         }
15129         this.sortToggle[f.name] = dir;
15130         this.sortInfo = {field: f.name, direction: dir};
15131         if(!this.remoteSort){
15132             this.applySort();
15133             this.fireEvent("datachanged", this);
15134         }else{
15135             this.load(this.lastOptions);
15136         }
15137     },
15138
15139     /**
15140      * Calls the specified function for each of the Records in the cache.
15141      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15142      * Returning <em>false</em> aborts and exits the iteration.
15143      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15144      */
15145     each : function(fn, scope){
15146         this.data.each(fn, scope);
15147     },
15148
15149     /**
15150      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15151      * (e.g., during paging).
15152      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15153      */
15154     getModifiedRecords : function(){
15155         return this.modified;
15156     },
15157
15158     // private
15159     createFilterFn : function(property, value, anyMatch){
15160         if(!value.exec){ // not a regex
15161             value = String(value);
15162             if(value.length == 0){
15163                 return false;
15164             }
15165             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15166         }
15167         return function(r){
15168             return value.test(r.data[property]);
15169         };
15170     },
15171
15172     /**
15173      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15174      * @param {String} property A field on your records
15175      * @param {Number} start The record index to start at (defaults to 0)
15176      * @param {Number} end The last record index to include (defaults to length - 1)
15177      * @return {Number} The sum
15178      */
15179     sum : function(property, start, end){
15180         var rs = this.data.items, v = 0;
15181         start = start || 0;
15182         end = (end || end === 0) ? end : rs.length-1;
15183
15184         for(var i = start; i <= end; i++){
15185             v += (rs[i].data[property] || 0);
15186         }
15187         return v;
15188     },
15189
15190     /**
15191      * Filter the records by a specified property.
15192      * @param {String} field A field on your records
15193      * @param {String/RegExp} value Either a string that the field
15194      * should start with or a RegExp to test against the field
15195      * @param {Boolean} anyMatch True to match any part not just the beginning
15196      */
15197     filter : function(property, value, anyMatch){
15198         var fn = this.createFilterFn(property, value, anyMatch);
15199         return fn ? this.filterBy(fn) : this.clearFilter();
15200     },
15201
15202     /**
15203      * Filter by a function. The specified function will be called with each
15204      * record in this data source. If the function returns true the record is included,
15205      * otherwise it is filtered.
15206      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15207      * @param {Object} scope (optional) The scope of the function (defaults to this)
15208      */
15209     filterBy : function(fn, scope){
15210         this.snapshot = this.snapshot || this.data;
15211         this.data = this.queryBy(fn, scope||this);
15212         this.fireEvent("datachanged", this);
15213     },
15214
15215     /**
15216      * Query the records by a specified property.
15217      * @param {String} field A field on your records
15218      * @param {String/RegExp} value Either a string that the field
15219      * should start with or a RegExp to test against the field
15220      * @param {Boolean} anyMatch True to match any part not just the beginning
15221      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15222      */
15223     query : function(property, value, anyMatch){
15224         var fn = this.createFilterFn(property, value, anyMatch);
15225         return fn ? this.queryBy(fn) : this.data.clone();
15226     },
15227
15228     /**
15229      * Query by a function. The specified function will be called with each
15230      * record in this data source. If the function returns true the record is included
15231      * in the results.
15232      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15233      * @param {Object} scope (optional) The scope of the function (defaults to this)
15234       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15235      **/
15236     queryBy : function(fn, scope){
15237         var data = this.snapshot || this.data;
15238         return data.filterBy(fn, scope||this);
15239     },
15240
15241     /**
15242      * Collects unique values for a particular dataIndex from this store.
15243      * @param {String} dataIndex The property to collect
15244      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15245      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15246      * @return {Array} An array of the unique values
15247      **/
15248     collect : function(dataIndex, allowNull, bypassFilter){
15249         var d = (bypassFilter === true && this.snapshot) ?
15250                 this.snapshot.items : this.data.items;
15251         var v, sv, r = [], l = {};
15252         for(var i = 0, len = d.length; i < len; i++){
15253             v = d[i].data[dataIndex];
15254             sv = String(v);
15255             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15256                 l[sv] = true;
15257                 r[r.length] = v;
15258             }
15259         }
15260         return r;
15261     },
15262
15263     /**
15264      * Revert to a view of the Record cache with no filtering applied.
15265      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15266      */
15267     clearFilter : function(suppressEvent){
15268         if(this.snapshot && this.snapshot != this.data){
15269             this.data = this.snapshot;
15270             delete this.snapshot;
15271             if(suppressEvent !== true){
15272                 this.fireEvent("datachanged", this);
15273             }
15274         }
15275     },
15276
15277     // private
15278     afterEdit : function(record){
15279         if(this.modified.indexOf(record) == -1){
15280             this.modified.push(record);
15281         }
15282         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15283     },
15284     
15285     // private
15286     afterReject : function(record){
15287         this.modified.remove(record);
15288         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15289     },
15290
15291     // private
15292     afterCommit : function(record){
15293         this.modified.remove(record);
15294         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15295     },
15296
15297     /**
15298      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15299      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15300      */
15301     commitChanges : function(){
15302         var m = this.modified.slice(0);
15303         this.modified = [];
15304         for(var i = 0, len = m.length; i < len; i++){
15305             m[i].commit();
15306         }
15307     },
15308
15309     /**
15310      * Cancel outstanding changes on all changed records.
15311      */
15312     rejectChanges : function(){
15313         var m = this.modified.slice(0);
15314         this.modified = [];
15315         for(var i = 0, len = m.length; i < len; i++){
15316             m[i].reject();
15317         }
15318     },
15319
15320     onMetaChange : function(meta, rtype, o){
15321         this.recordType = rtype;
15322         this.fields = rtype.prototype.fields;
15323         delete this.snapshot;
15324         this.sortInfo = meta.sortInfo || this.sortInfo;
15325         this.modified = [];
15326         this.fireEvent('metachange', this, this.reader.meta);
15327     },
15328     
15329     moveIndex : function(data, type)
15330     {
15331         var index = this.indexOf(data);
15332         
15333         var newIndex = index + type;
15334         
15335         this.remove(data);
15336         
15337         this.insert(newIndex, data);
15338         
15339     }
15340 });/*
15341  * Based on:
15342  * Ext JS Library 1.1.1
15343  * Copyright(c) 2006-2007, Ext JS, LLC.
15344  *
15345  * Originally Released Under LGPL - original licence link has changed is not relivant.
15346  *
15347  * Fork - LGPL
15348  * <script type="text/javascript">
15349  */
15350
15351 /**
15352  * @class Roo.data.SimpleStore
15353  * @extends Roo.data.Store
15354  * Small helper class to make creating Stores from Array data easier.
15355  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15356  * @cfg {Array} fields An array of field definition objects, or field name strings.
15357  * @cfg {Object} an existing reader (eg. copied from another store)
15358  * @cfg {Array} data The multi-dimensional array of data
15359  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15360  * @cfg {Roo.data.Reader} reader  [not-required] 
15361  * @constructor
15362  * @param {Object} config
15363  */
15364 Roo.data.SimpleStore = function(config)
15365 {
15366     Roo.data.SimpleStore.superclass.constructor.call(this, {
15367         isLocal : true,
15368         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15369                 id: config.id
15370             },
15371             Roo.data.Record.create(config.fields)
15372         ),
15373         proxy : new Roo.data.MemoryProxy(config.data)
15374     });
15375     this.load();
15376 };
15377 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15378  * Based on:
15379  * Ext JS Library 1.1.1
15380  * Copyright(c) 2006-2007, Ext JS, LLC.
15381  *
15382  * Originally Released Under LGPL - original licence link has changed is not relivant.
15383  *
15384  * Fork - LGPL
15385  * <script type="text/javascript">
15386  */
15387
15388 /**
15389 /**
15390  * @extends Roo.data.Store
15391  * @class Roo.data.JsonStore
15392  * Small helper class to make creating Stores for JSON data easier. <br/>
15393 <pre><code>
15394 var store = new Roo.data.JsonStore({
15395     url: 'get-images.php',
15396     root: 'images',
15397     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15398 });
15399 </code></pre>
15400  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15401  * JsonReader and HttpProxy (unless inline data is provided).</b>
15402  * @cfg {Array} fields An array of field definition objects, or field name strings.
15403  * @constructor
15404  * @param {Object} config
15405  */
15406 Roo.data.JsonStore = function(c){
15407     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15408         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15409         reader: new Roo.data.JsonReader(c, c.fields)
15410     }));
15411 };
15412 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15413  * Based on:
15414  * Ext JS Library 1.1.1
15415  * Copyright(c) 2006-2007, Ext JS, LLC.
15416  *
15417  * Originally Released Under LGPL - original licence link has changed is not relivant.
15418  *
15419  * Fork - LGPL
15420  * <script type="text/javascript">
15421  */
15422
15423  
15424 Roo.data.Field = function(config){
15425     if(typeof config == "string"){
15426         config = {name: config};
15427     }
15428     Roo.apply(this, config);
15429     
15430     if(!this.type){
15431         this.type = "auto";
15432     }
15433     
15434     var st = Roo.data.SortTypes;
15435     // named sortTypes are supported, here we look them up
15436     if(typeof this.sortType == "string"){
15437         this.sortType = st[this.sortType];
15438     }
15439     
15440     // set default sortType for strings and dates
15441     if(!this.sortType){
15442         switch(this.type){
15443             case "string":
15444                 this.sortType = st.asUCString;
15445                 break;
15446             case "date":
15447                 this.sortType = st.asDate;
15448                 break;
15449             default:
15450                 this.sortType = st.none;
15451         }
15452     }
15453
15454     // define once
15455     var stripRe = /[\$,%]/g;
15456
15457     // prebuilt conversion function for this field, instead of
15458     // switching every time we're reading a value
15459     if(!this.convert){
15460         var cv, dateFormat = this.dateFormat;
15461         switch(this.type){
15462             case "":
15463             case "auto":
15464             case undefined:
15465                 cv = function(v){ return v; };
15466                 break;
15467             case "string":
15468                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15469                 break;
15470             case "int":
15471                 cv = function(v){
15472                     return v !== undefined && v !== null && v !== '' ?
15473                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15474                     };
15475                 break;
15476             case "float":
15477                 cv = function(v){
15478                     return v !== undefined && v !== null && v !== '' ?
15479                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15480                     };
15481                 break;
15482             case "bool":
15483             case "boolean":
15484                 cv = function(v){ return v === true || v === "true" || v == 1; };
15485                 break;
15486             case "date":
15487                 cv = function(v){
15488                     if(!v){
15489                         return '';
15490                     }
15491                     if(v instanceof Date){
15492                         return v;
15493                     }
15494                     if(dateFormat){
15495                         if(dateFormat == "timestamp"){
15496                             return new Date(v*1000);
15497                         }
15498                         return Date.parseDate(v, dateFormat);
15499                     }
15500                     var parsed = Date.parse(v);
15501                     return parsed ? new Date(parsed) : null;
15502                 };
15503              break;
15504             
15505         }
15506         this.convert = cv;
15507     }
15508 };
15509
15510 Roo.data.Field.prototype = {
15511     dateFormat: null,
15512     defaultValue: "",
15513     mapping: null,
15514     sortType : null,
15515     sortDir : "ASC"
15516 };/*
15517  * Based on:
15518  * Ext JS Library 1.1.1
15519  * Copyright(c) 2006-2007, Ext JS, LLC.
15520  *
15521  * Originally Released Under LGPL - original licence link has changed is not relivant.
15522  *
15523  * Fork - LGPL
15524  * <script type="text/javascript">
15525  */
15526  
15527 // Base class for reading structured data from a data source.  This class is intended to be
15528 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15529
15530 /**
15531  * @class Roo.data.DataReader
15532  * @abstract
15533  * Base class for reading structured data from a data source.  This class is intended to be
15534  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15535  */
15536
15537 Roo.data.DataReader = function(meta, recordType){
15538     
15539     this.meta = meta;
15540     
15541     this.recordType = recordType instanceof Array ? 
15542         Roo.data.Record.create(recordType) : recordType;
15543 };
15544
15545 Roo.data.DataReader.prototype = {
15546     
15547     
15548     readerType : 'Data',
15549      /**
15550      * Create an empty record
15551      * @param {Object} data (optional) - overlay some values
15552      * @return {Roo.data.Record} record created.
15553      */
15554     newRow :  function(d) {
15555         var da =  {};
15556         this.recordType.prototype.fields.each(function(c) {
15557             switch( c.type) {
15558                 case 'int' : da[c.name] = 0; break;
15559                 case 'date' : da[c.name] = new Date(); break;
15560                 case 'float' : da[c.name] = 0.0; break;
15561                 case 'boolean' : da[c.name] = false; break;
15562                 default : da[c.name] = ""; break;
15563             }
15564             
15565         });
15566         return new this.recordType(Roo.apply(da, d));
15567     }
15568     
15569     
15570 };/*
15571  * Based on:
15572  * Ext JS Library 1.1.1
15573  * Copyright(c) 2006-2007, Ext JS, LLC.
15574  *
15575  * Originally Released Under LGPL - original licence link has changed is not relivant.
15576  *
15577  * Fork - LGPL
15578  * <script type="text/javascript">
15579  */
15580
15581 /**
15582  * @class Roo.data.DataProxy
15583  * @extends Roo.data.Observable
15584  * @abstract
15585  * This class is an abstract base class for implementations which provide retrieval of
15586  * unformatted data objects.<br>
15587  * <p>
15588  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15589  * (of the appropriate type which knows how to parse the data object) to provide a block of
15590  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15591  * <p>
15592  * Custom implementations must implement the load method as described in
15593  * {@link Roo.data.HttpProxy#load}.
15594  */
15595 Roo.data.DataProxy = function(){
15596     this.addEvents({
15597         /**
15598          * @event beforeload
15599          * Fires before a network request is made to retrieve a data object.
15600          * @param {Object} This DataProxy object.
15601          * @param {Object} params The params parameter to the load function.
15602          */
15603         beforeload : true,
15604         /**
15605          * @event load
15606          * Fires before the load method's callback is called.
15607          * @param {Object} This DataProxy object.
15608          * @param {Object} o The data object.
15609          * @param {Object} arg The callback argument object passed to the load function.
15610          */
15611         load : true,
15612         /**
15613          * @event loadexception
15614          * Fires if an Exception occurs during data retrieval.
15615          * @param {Object} This DataProxy object.
15616          * @param {Object} o The data object.
15617          * @param {Object} arg The callback argument object passed to the load function.
15618          * @param {Object} e The Exception.
15619          */
15620         loadexception : true
15621     });
15622     Roo.data.DataProxy.superclass.constructor.call(this);
15623 };
15624
15625 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15626
15627     /**
15628      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15629      */
15630 /*
15631  * Based on:
15632  * Ext JS Library 1.1.1
15633  * Copyright(c) 2006-2007, Ext JS, LLC.
15634  *
15635  * Originally Released Under LGPL - original licence link has changed is not relivant.
15636  *
15637  * Fork - LGPL
15638  * <script type="text/javascript">
15639  */
15640 /**
15641  * @class Roo.data.MemoryProxy
15642  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15643  * to the Reader when its load method is called.
15644  * @constructor
15645  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15646  */
15647 Roo.data.MemoryProxy = function(data){
15648     if (data.data) {
15649         data = data.data;
15650     }
15651     Roo.data.MemoryProxy.superclass.constructor.call(this);
15652     this.data = data;
15653 };
15654
15655 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15656     
15657     /**
15658      * Load data from the requested source (in this case an in-memory
15659      * data object passed to the constructor), read the data object into
15660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15661      * process that block using the passed callback.
15662      * @param {Object} params This parameter is not used by the MemoryProxy class.
15663      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15664      * object into a block of Roo.data.Records.
15665      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15666      * The function must be passed <ul>
15667      * <li>The Record block object</li>
15668      * <li>The "arg" argument from the load function</li>
15669      * <li>A boolean success indicator</li>
15670      * </ul>
15671      * @param {Object} scope The scope in which to call the callback
15672      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15673      */
15674     load : function(params, reader, callback, scope, arg){
15675         params = params || {};
15676         var result;
15677         try {
15678             result = reader.readRecords(params.data ? params.data :this.data);
15679         }catch(e){
15680             this.fireEvent("loadexception", this, arg, null, e);
15681             callback.call(scope, null, arg, false);
15682             return;
15683         }
15684         callback.call(scope, result, arg, true);
15685     },
15686     
15687     // private
15688     update : function(params, records){
15689         
15690     }
15691 });/*
15692  * Based on:
15693  * Ext JS Library 1.1.1
15694  * Copyright(c) 2006-2007, Ext JS, LLC.
15695  *
15696  * Originally Released Under LGPL - original licence link has changed is not relivant.
15697  *
15698  * Fork - LGPL
15699  * <script type="text/javascript">
15700  */
15701 /**
15702  * @class Roo.data.HttpProxy
15703  * @extends Roo.data.DataProxy
15704  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15705  * configured to reference a certain URL.<br><br>
15706  * <p>
15707  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15708  * from which the running page was served.<br><br>
15709  * <p>
15710  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15711  * <p>
15712  * Be aware that to enable the browser to parse an XML document, the server must set
15713  * the Content-Type header in the HTTP response to "text/xml".
15714  * @constructor
15715  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15716  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15717  * will be used to make the request.
15718  */
15719 Roo.data.HttpProxy = function(conn){
15720     Roo.data.HttpProxy.superclass.constructor.call(this);
15721     // is conn a conn config or a real conn?
15722     this.conn = conn;
15723     this.useAjax = !conn || !conn.events;
15724   
15725 };
15726
15727 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15728     // thse are take from connection...
15729     
15730     /**
15731      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15732      */
15733     /**
15734      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15735      * extra parameters to each request made by this object. (defaults to undefined)
15736      */
15737     /**
15738      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15739      *  to each request made by this object. (defaults to undefined)
15740      */
15741     /**
15742      * @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)
15743      */
15744     /**
15745      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15746      */
15747      /**
15748      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15749      * @type Boolean
15750      */
15751   
15752
15753     /**
15754      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15755      * @type Boolean
15756      */
15757     /**
15758      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15759      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15760      * a finer-grained basis than the DataProxy events.
15761      */
15762     getConnection : function(){
15763         return this.useAjax ? Roo.Ajax : this.conn;
15764     },
15765
15766     /**
15767      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15768      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15769      * process that block using the passed callback.
15770      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15771      * for the request to the remote server.
15772      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15773      * object into a block of Roo.data.Records.
15774      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15775      * The function must be passed <ul>
15776      * <li>The Record block object</li>
15777      * <li>The "arg" argument from the load function</li>
15778      * <li>A boolean success indicator</li>
15779      * </ul>
15780      * @param {Object} scope The scope in which to call the callback
15781      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15782      */
15783     load : function(params, reader, callback, scope, arg){
15784         if(this.fireEvent("beforeload", this, params) !== false){
15785             var  o = {
15786                 params : params || {},
15787                 request: {
15788                     callback : callback,
15789                     scope : scope,
15790                     arg : arg
15791                 },
15792                 reader: reader,
15793                 callback : this.loadResponse,
15794                 scope: this
15795             };
15796             if(this.useAjax){
15797                 Roo.applyIf(o, this.conn);
15798                 if(this.activeRequest){
15799                     Roo.Ajax.abort(this.activeRequest);
15800                 }
15801                 this.activeRequest = Roo.Ajax.request(o);
15802             }else{
15803                 this.conn.request(o);
15804             }
15805         }else{
15806             callback.call(scope||this, null, arg, false);
15807         }
15808     },
15809
15810     // private
15811     loadResponse : function(o, success, response){
15812         delete this.activeRequest;
15813         if(!success){
15814             this.fireEvent("loadexception", this, o, response);
15815             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15816             return;
15817         }
15818         var result;
15819         try {
15820             result = o.reader.read(response);
15821         }catch(e){
15822             this.fireEvent("loadexception", this, o, response, e);
15823             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15824             return;
15825         }
15826         
15827         this.fireEvent("load", this, o, o.request.arg);
15828         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15829     },
15830
15831     // private
15832     update : function(dataSet){
15833
15834     },
15835
15836     // private
15837     updateResponse : function(dataSet){
15838
15839     }
15840 });/*
15841  * Based on:
15842  * Ext JS Library 1.1.1
15843  * Copyright(c) 2006-2007, Ext JS, LLC.
15844  *
15845  * Originally Released Under LGPL - original licence link has changed is not relivant.
15846  *
15847  * Fork - LGPL
15848  * <script type="text/javascript">
15849  */
15850
15851 /**
15852  * @class Roo.data.ScriptTagProxy
15853  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15854  * other than the originating domain of the running page.<br><br>
15855  * <p>
15856  * <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
15857  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15858  * <p>
15859  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15860  * source code that is used as the source inside a &lt;script> tag.<br><br>
15861  * <p>
15862  * In order for the browser to process the returned data, the server must wrap the data object
15863  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15864  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15865  * depending on whether the callback name was passed:
15866  * <p>
15867  * <pre><code>
15868 boolean scriptTag = false;
15869 String cb = request.getParameter("callback");
15870 if (cb != null) {
15871     scriptTag = true;
15872     response.setContentType("text/javascript");
15873 } else {
15874     response.setContentType("application/x-json");
15875 }
15876 Writer out = response.getWriter();
15877 if (scriptTag) {
15878     out.write(cb + "(");
15879 }
15880 out.print(dataBlock.toJsonString());
15881 if (scriptTag) {
15882     out.write(");");
15883 }
15884 </pre></code>
15885  *
15886  * @constructor
15887  * @param {Object} config A configuration object.
15888  */
15889 Roo.data.ScriptTagProxy = function(config){
15890     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15891     Roo.apply(this, config);
15892     this.head = document.getElementsByTagName("head")[0];
15893 };
15894
15895 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15896
15897 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15898     /**
15899      * @cfg {String} url The URL from which to request the data object.
15900      */
15901     /**
15902      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15903      */
15904     timeout : 30000,
15905     /**
15906      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15907      * the server the name of the callback function set up by the load call to process the returned data object.
15908      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15909      * javascript output which calls this named function passing the data object as its only parameter.
15910      */
15911     callbackParam : "callback",
15912     /**
15913      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15914      * name to the request.
15915      */
15916     nocache : true,
15917
15918     /**
15919      * Load data from the configured URL, read the data object into
15920      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15921      * process that block using the passed callback.
15922      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15923      * for the request to the remote server.
15924      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15925      * object into a block of Roo.data.Records.
15926      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15927      * The function must be passed <ul>
15928      * <li>The Record block object</li>
15929      * <li>The "arg" argument from the load function</li>
15930      * <li>A boolean success indicator</li>
15931      * </ul>
15932      * @param {Object} scope The scope in which to call the callback
15933      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15934      */
15935     load : function(params, reader, callback, scope, arg){
15936         if(this.fireEvent("beforeload", this, params) !== false){
15937
15938             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15939
15940             var url = this.url;
15941             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15942             if(this.nocache){
15943                 url += "&_dc=" + (new Date().getTime());
15944             }
15945             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15946             var trans = {
15947                 id : transId,
15948                 cb : "stcCallback"+transId,
15949                 scriptId : "stcScript"+transId,
15950                 params : params,
15951                 arg : arg,
15952                 url : url,
15953                 callback : callback,
15954                 scope : scope,
15955                 reader : reader
15956             };
15957             var conn = this;
15958
15959             window[trans.cb] = function(o){
15960                 conn.handleResponse(o, trans);
15961             };
15962
15963             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15964
15965             if(this.autoAbort !== false){
15966                 this.abort();
15967             }
15968
15969             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15970
15971             var script = document.createElement("script");
15972             script.setAttribute("src", url);
15973             script.setAttribute("type", "text/javascript");
15974             script.setAttribute("id", trans.scriptId);
15975             this.head.appendChild(script);
15976
15977             this.trans = trans;
15978         }else{
15979             callback.call(scope||this, null, arg, false);
15980         }
15981     },
15982
15983     // private
15984     isLoading : function(){
15985         return this.trans ? true : false;
15986     },
15987
15988     /**
15989      * Abort the current server request.
15990      */
15991     abort : function(){
15992         if(this.isLoading()){
15993             this.destroyTrans(this.trans);
15994         }
15995     },
15996
15997     // private
15998     destroyTrans : function(trans, isLoaded){
15999         this.head.removeChild(document.getElementById(trans.scriptId));
16000         clearTimeout(trans.timeoutId);
16001         if(isLoaded){
16002             window[trans.cb] = undefined;
16003             try{
16004                 delete window[trans.cb];
16005             }catch(e){}
16006         }else{
16007             // if hasn't been loaded, wait for load to remove it to prevent script error
16008             window[trans.cb] = function(){
16009                 window[trans.cb] = undefined;
16010                 try{
16011                     delete window[trans.cb];
16012                 }catch(e){}
16013             };
16014         }
16015     },
16016
16017     // private
16018     handleResponse : function(o, trans){
16019         this.trans = false;
16020         this.destroyTrans(trans, true);
16021         var result;
16022         try {
16023             result = trans.reader.readRecords(o);
16024         }catch(e){
16025             this.fireEvent("loadexception", this, o, trans.arg, e);
16026             trans.callback.call(trans.scope||window, null, trans.arg, false);
16027             return;
16028         }
16029         this.fireEvent("load", this, o, trans.arg);
16030         trans.callback.call(trans.scope||window, result, trans.arg, true);
16031     },
16032
16033     // private
16034     handleFailure : function(trans){
16035         this.trans = false;
16036         this.destroyTrans(trans, false);
16037         this.fireEvent("loadexception", this, null, trans.arg);
16038         trans.callback.call(trans.scope||window, null, trans.arg, false);
16039     }
16040 });/*
16041  * Based on:
16042  * Ext JS Library 1.1.1
16043  * Copyright(c) 2006-2007, Ext JS, LLC.
16044  *
16045  * Originally Released Under LGPL - original licence link has changed is not relivant.
16046  *
16047  * Fork - LGPL
16048  * <script type="text/javascript">
16049  */
16050
16051 /**
16052  * @class Roo.data.JsonReader
16053  * @extends Roo.data.DataReader
16054  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16055  * based on mappings in a provided Roo.data.Record constructor.
16056  * 
16057  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16058  * in the reply previously. 
16059  * 
16060  * <p>
16061  * Example code:
16062  * <pre><code>
16063 var RecordDef = Roo.data.Record.create([
16064     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16065     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16066 ]);
16067 var myReader = new Roo.data.JsonReader({
16068     totalProperty: "results",    // The property which contains the total dataset size (optional)
16069     root: "rows",                // The property which contains an Array of row objects
16070     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16071 }, RecordDef);
16072 </code></pre>
16073  * <p>
16074  * This would consume a JSON file like this:
16075  * <pre><code>
16076 { 'results': 2, 'rows': [
16077     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16078     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16079 }
16080 </code></pre>
16081  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16082  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16083  * paged from the remote server.
16084  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16085  * @cfg {String} root name of the property which contains the Array of row objects.
16086  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16087  * @cfg {Array} fields Array of field definition objects
16088  * @constructor
16089  * Create a new JsonReader
16090  * @param {Object} meta Metadata configuration options
16091  * @param {Object} recordType Either an Array of field definition objects,
16092  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16093  */
16094 Roo.data.JsonReader = function(meta, recordType){
16095     
16096     meta = meta || {};
16097     // set some defaults:
16098     Roo.applyIf(meta, {
16099         totalProperty: 'total',
16100         successProperty : 'success',
16101         root : 'data',
16102         id : 'id'
16103     });
16104     
16105     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16106 };
16107 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16108     
16109     readerType : 'Json',
16110     
16111     /**
16112      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16113      * Used by Store query builder to append _requestMeta to params.
16114      * 
16115      */
16116     metaFromRemote : false,
16117     /**
16118      * This method is only used by a DataProxy which has retrieved data from a remote server.
16119      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16120      * @return {Object} data A data block which is used by an Roo.data.Store object as
16121      * a cache of Roo.data.Records.
16122      */
16123     read : function(response){
16124         var json = response.responseText;
16125        
16126         var o = /* eval:var:o */ eval("("+json+")");
16127         if(!o) {
16128             throw {message: "JsonReader.read: Json object not found"};
16129         }
16130         
16131         if(o.metaData){
16132             
16133             delete this.ef;
16134             this.metaFromRemote = true;
16135             this.meta = o.metaData;
16136             this.recordType = Roo.data.Record.create(o.metaData.fields);
16137             this.onMetaChange(this.meta, this.recordType, o);
16138         }
16139         return this.readRecords(o);
16140     },
16141
16142     // private function a store will implement
16143     onMetaChange : function(meta, recordType, o){
16144
16145     },
16146
16147     /**
16148          * @ignore
16149          */
16150     simpleAccess: function(obj, subsc) {
16151         return obj[subsc];
16152     },
16153
16154         /**
16155          * @ignore
16156          */
16157     getJsonAccessor: function(){
16158         var re = /[\[\.]/;
16159         return function(expr) {
16160             try {
16161                 return(re.test(expr))
16162                     ? new Function("obj", "return obj." + expr)
16163                     : function(obj){
16164                         return obj[expr];
16165                     };
16166             } catch(e){}
16167             return Roo.emptyFn;
16168         };
16169     }(),
16170
16171     /**
16172      * Create a data block containing Roo.data.Records from an XML document.
16173      * @param {Object} o An object which contains an Array of row objects in the property specified
16174      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16175      * which contains the total size of the dataset.
16176      * @return {Object} data A data block which is used by an Roo.data.Store object as
16177      * a cache of Roo.data.Records.
16178      */
16179     readRecords : function(o){
16180         /**
16181          * After any data loads, the raw JSON data is available for further custom processing.
16182          * @type Object
16183          */
16184         this.o = o;
16185         var s = this.meta, Record = this.recordType,
16186             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16187
16188 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16189         if (!this.ef) {
16190             if(s.totalProperty) {
16191                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16192                 }
16193                 if(s.successProperty) {
16194                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16195                 }
16196                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16197                 if (s.id) {
16198                         var g = this.getJsonAccessor(s.id);
16199                         this.getId = function(rec) {
16200                                 var r = g(rec);  
16201                                 return (r === undefined || r === "") ? null : r;
16202                         };
16203                 } else {
16204                         this.getId = function(){return null;};
16205                 }
16206             this.ef = [];
16207             for(var jj = 0; jj < fl; jj++){
16208                 f = fi[jj];
16209                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16210                 this.ef[jj] = this.getJsonAccessor(map);
16211             }
16212         }
16213
16214         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16215         if(s.totalProperty){
16216             var vt = parseInt(this.getTotal(o), 10);
16217             if(!isNaN(vt)){
16218                 totalRecords = vt;
16219             }
16220         }
16221         if(s.successProperty){
16222             var vs = this.getSuccess(o);
16223             if(vs === false || vs === 'false'){
16224                 success = false;
16225             }
16226         }
16227         var records = [];
16228         for(var i = 0; i < c; i++){
16229                 var n = root[i];
16230             var values = {};
16231             var id = this.getId(n);
16232             for(var j = 0; j < fl; j++){
16233                 f = fi[j];
16234             var v = this.ef[j](n);
16235             if (!f.convert) {
16236                 Roo.log('missing convert for ' + f.name);
16237                 Roo.log(f);
16238                 continue;
16239             }
16240             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16241             }
16242             var record = new Record(values, id);
16243             record.json = n;
16244             records[i] = record;
16245         }
16246         return {
16247             raw : o,
16248             success : success,
16249             records : records,
16250             totalRecords : totalRecords
16251         };
16252     },
16253     // used when loading children.. @see loadDataFromChildren
16254     toLoadData: function(rec)
16255     {
16256         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16257         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16258         return { data : data, total : data.length };
16259         
16260     }
16261 });/*
16262  * Based on:
16263  * Ext JS Library 1.1.1
16264  * Copyright(c) 2006-2007, Ext JS, LLC.
16265  *
16266  * Originally Released Under LGPL - original licence link has changed is not relivant.
16267  *
16268  * Fork - LGPL
16269  * <script type="text/javascript">
16270  */
16271
16272 /**
16273  * @class Roo.data.ArrayReader
16274  * @extends Roo.data.DataReader
16275  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16276  * Each element of that Array represents a row of data fields. The
16277  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16278  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16279  * <p>
16280  * Example code:.
16281  * <pre><code>
16282 var RecordDef = Roo.data.Record.create([
16283     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16284     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16285 ]);
16286 var myReader = new Roo.data.ArrayReader({
16287     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16288 }, RecordDef);
16289 </code></pre>
16290  * <p>
16291  * This would consume an Array like this:
16292  * <pre><code>
16293 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16294   </code></pre>
16295  
16296  * @constructor
16297  * Create a new JsonReader
16298  * @param {Object} meta Metadata configuration options.
16299  * @param {Object|Array} recordType Either an Array of field definition objects
16300  * 
16301  * @cfg {Array} fields Array of field definition objects
16302  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16303  * as specified to {@link Roo.data.Record#create},
16304  * or an {@link Roo.data.Record} object
16305  *
16306  * 
16307  * created using {@link Roo.data.Record#create}.
16308  */
16309 Roo.data.ArrayReader = function(meta, recordType)
16310 {    
16311     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16312 };
16313
16314 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16315     
16316       /**
16317      * Create a data block containing Roo.data.Records from an XML document.
16318      * @param {Object} o An Array of row objects which represents the dataset.
16319      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16320      * a cache of Roo.data.Records.
16321      */
16322     readRecords : function(o)
16323     {
16324         var sid = this.meta ? this.meta.id : null;
16325         var recordType = this.recordType, fields = recordType.prototype.fields;
16326         var records = [];
16327         var root = o;
16328         for(var i = 0; i < root.length; i++){
16329             var n = root[i];
16330             var values = {};
16331             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16332             for(var j = 0, jlen = fields.length; j < jlen; j++){
16333                 var f = fields.items[j];
16334                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16335                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16336                 v = f.convert(v);
16337                 values[f.name] = v;
16338             }
16339             var record = new recordType(values, id);
16340             record.json = n;
16341             records[records.length] = record;
16342         }
16343         return {
16344             records : records,
16345             totalRecords : records.length
16346         };
16347     },
16348     // used when loading children.. @see loadDataFromChildren
16349     toLoadData: function(rec)
16350     {
16351         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16352         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16353         
16354     }
16355     
16356     
16357 });/*
16358  * - LGPL
16359  * * 
16360  */
16361
16362 /**
16363  * @class Roo.bootstrap.ComboBox
16364  * @extends Roo.bootstrap.TriggerField
16365  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16366  * @cfg {Boolean} append (true|false) default false
16367  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16368  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16369  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16370  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16371  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16372  * @cfg {Boolean} animate default true
16373  * @cfg {Boolean} emptyResultText only for touch device
16374  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16375  * @cfg {String} emptyTitle default ''
16376  * @cfg {Number} width fixed with? experimental
16377  * @constructor
16378  * Create a new ComboBox.
16379  * @param {Object} config Configuration options
16380  */
16381 Roo.bootstrap.ComboBox = function(config){
16382     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16383     this.addEvents({
16384         /**
16385          * @event expand
16386          * Fires when the dropdown list is expanded
16387         * @param {Roo.bootstrap.ComboBox} combo This combo box
16388         */
16389         'expand' : true,
16390         /**
16391          * @event collapse
16392          * Fires when the dropdown list is collapsed
16393         * @param {Roo.bootstrap.ComboBox} combo This combo box
16394         */
16395         'collapse' : true,
16396         /**
16397          * @event beforeselect
16398          * Fires before a list item is selected. Return false to cancel the selection.
16399         * @param {Roo.bootstrap.ComboBox} combo This combo box
16400         * @param {Roo.data.Record} record The data record returned from the underlying store
16401         * @param {Number} index The index of the selected item in the dropdown list
16402         */
16403         'beforeselect' : true,
16404         /**
16405          * @event select
16406          * Fires when a list item is selected
16407         * @param {Roo.bootstrap.ComboBox} combo This combo box
16408         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16409         * @param {Number} index The index of the selected item in the dropdown list
16410         */
16411         'select' : true,
16412         /**
16413          * @event beforequery
16414          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16415          * The event object passed has these properties:
16416         * @param {Roo.bootstrap.ComboBox} combo This combo box
16417         * @param {String} query The query
16418         * @param {Boolean} forceAll true to force "all" query
16419         * @param {Boolean} cancel true to cancel the query
16420         * @param {Object} e The query event object
16421         */
16422         'beforequery': true,
16423          /**
16424          * @event add
16425          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16426         * @param {Roo.bootstrap.ComboBox} combo This combo box
16427         */
16428         'add' : true,
16429         /**
16430          * @event edit
16431          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16432         * @param {Roo.bootstrap.ComboBox} combo This combo box
16433         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16434         */
16435         'edit' : true,
16436         /**
16437          * @event remove
16438          * Fires when the remove value from the combobox array
16439         * @param {Roo.bootstrap.ComboBox} combo This combo box
16440         */
16441         'remove' : true,
16442         /**
16443          * @event afterremove
16444          * Fires when the remove value from the combobox array
16445         * @param {Roo.bootstrap.ComboBox} combo This combo box
16446         */
16447         'afterremove' : true,
16448         /**
16449          * @event specialfilter
16450          * Fires when specialfilter
16451             * @param {Roo.bootstrap.ComboBox} combo This combo box
16452             */
16453         'specialfilter' : true,
16454         /**
16455          * @event tick
16456          * Fires when tick the element
16457             * @param {Roo.bootstrap.ComboBox} combo This combo box
16458             */
16459         'tick' : true,
16460         /**
16461          * @event touchviewdisplay
16462          * Fires when touch view require special display (default is using displayField)
16463             * @param {Roo.bootstrap.ComboBox} combo This combo box
16464             * @param {Object} cfg set html .
16465             */
16466         'touchviewdisplay' : true
16467         
16468     });
16469     
16470     this.item = [];
16471     this.tickItems = [];
16472     
16473     this.selectedIndex = -1;
16474     if(this.mode == 'local'){
16475         if(config.queryDelay === undefined){
16476             this.queryDelay = 10;
16477         }
16478         if(config.minChars === undefined){
16479             this.minChars = 0;
16480         }
16481     }
16482 };
16483
16484 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16485      
16486     /**
16487      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16488      * rendering into an Roo.Editor, defaults to false)
16489      */
16490     /**
16491      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16492      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16493      */
16494     /**
16495      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16496      */
16497     /**
16498      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16499      * the dropdown list (defaults to undefined, with no header element)
16500      */
16501
16502      /**
16503      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16504      */
16505      
16506      /**
16507      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16508      */
16509     listWidth: undefined,
16510     /**
16511      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16512      * mode = 'remote' or 'text' if mode = 'local')
16513      */
16514     displayField: undefined,
16515     
16516     /**
16517      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16518      * mode = 'remote' or 'value' if mode = 'local'). 
16519      * Note: use of a valueField requires the user make a selection
16520      * in order for a value to be mapped.
16521      */
16522     valueField: undefined,
16523     /**
16524      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16525      */
16526     modalTitle : '',
16527     
16528     /**
16529      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16530      * field's data value (defaults to the underlying DOM element's name)
16531      */
16532     hiddenName: undefined,
16533     /**
16534      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16535      */
16536     listClass: '',
16537     /**
16538      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16539      */
16540     selectedClass: 'active',
16541     
16542     /**
16543      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16544      */
16545     shadow:'sides',
16546     /**
16547      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16548      * anchor positions (defaults to 'tl-bl')
16549      */
16550     listAlign: 'tl-bl?',
16551     /**
16552      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16553      */
16554     maxHeight: 300,
16555     /**
16556      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16557      * query specified by the allQuery config option (defaults to 'query')
16558      */
16559     triggerAction: 'query',
16560     /**
16561      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16562      * (defaults to 4, does not apply if editable = false)
16563      */
16564     minChars : 4,
16565     /**
16566      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16567      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16568      */
16569     typeAhead: false,
16570     /**
16571      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16572      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16573      */
16574     queryDelay: 500,
16575     /**
16576      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16577      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16578      */
16579     pageSize: 0,
16580     /**
16581      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16582      * when editable = true (defaults to false)
16583      */
16584     selectOnFocus:false,
16585     /**
16586      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16587      */
16588     queryParam: 'query',
16589     /**
16590      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16591      * when mode = 'remote' (defaults to 'Loading...')
16592      */
16593     loadingText: 'Loading...',
16594     /**
16595      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16596      */
16597     resizable: false,
16598     /**
16599      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16600      */
16601     handleHeight : 8,
16602     /**
16603      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16604      * traditional select (defaults to true)
16605      */
16606     editable: true,
16607     /**
16608      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16609      */
16610     allQuery: '',
16611     /**
16612      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16613      */
16614     mode: 'remote',
16615     /**
16616      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16617      * listWidth has a higher value)
16618      */
16619     minListWidth : 70,
16620     /**
16621      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16622      * allow the user to set arbitrary text into the field (defaults to false)
16623      */
16624     forceSelection:false,
16625     /**
16626      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16627      * if typeAhead = true (defaults to 250)
16628      */
16629     typeAheadDelay : 250,
16630     /**
16631      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16632      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16633      */
16634     valueNotFoundText : undefined,
16635     /**
16636      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16637      */
16638     blockFocus : false,
16639     
16640     /**
16641      * @cfg {Boolean} disableClear Disable showing of clear button.
16642      */
16643     disableClear : false,
16644     /**
16645      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16646      */
16647     alwaysQuery : false,
16648     
16649     /**
16650      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16651      */
16652     multiple : false,
16653     
16654     /**
16655      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16656      */
16657     invalidClass : "has-warning",
16658     
16659     /**
16660      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16661      */
16662     validClass : "has-success",
16663     
16664     /**
16665      * @cfg {Boolean} specialFilter (true|false) special filter default false
16666      */
16667     specialFilter : false,
16668     
16669     /**
16670      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16671      */
16672     mobileTouchView : true,
16673     
16674     /**
16675      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16676      */
16677     useNativeIOS : false,
16678     
16679     /**
16680      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16681      */
16682     mobile_restrict_height : false,
16683     
16684     ios_options : false,
16685     
16686     //private
16687     addicon : false,
16688     editicon: false,
16689     
16690     page: 0,
16691     hasQuery: false,
16692     append: false,
16693     loadNext: false,
16694     autoFocus : true,
16695     tickable : false,
16696     btnPosition : 'right',
16697     triggerList : true,
16698     showToggleBtn : true,
16699     animate : true,
16700     emptyResultText: 'Empty',
16701     triggerText : 'Select',
16702     emptyTitle : '',
16703     width : false,
16704     
16705     // element that contains real text value.. (when hidden is used..)
16706     
16707     getAutoCreate : function()
16708     {   
16709         var cfg = false;
16710         //render
16711         /*
16712          * Render classic select for iso
16713          */
16714         
16715         if(Roo.isIOS && this.useNativeIOS){
16716             cfg = this.getAutoCreateNativeIOS();
16717             return cfg;
16718         }
16719         
16720         /*
16721          * Touch Devices
16722          */
16723         
16724         if(Roo.isTouch && this.mobileTouchView){
16725             cfg = this.getAutoCreateTouchView();
16726             return cfg;;
16727         }
16728         
16729         /*
16730          *  Normal ComboBox
16731          */
16732         if(!this.tickable){
16733             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16734             return cfg;
16735         }
16736         
16737         /*
16738          *  ComboBox with tickable selections
16739          */
16740              
16741         var align = this.labelAlign || this.parentLabelAlign();
16742         
16743         cfg = {
16744             cls : 'form-group roo-combobox-tickable' //input-group
16745         };
16746         
16747         var btn_text_select = '';
16748         var btn_text_done = '';
16749         var btn_text_cancel = '';
16750         
16751         if (this.btn_text_show) {
16752             btn_text_select = 'Select';
16753             btn_text_done = 'Done';
16754             btn_text_cancel = 'Cancel'; 
16755         }
16756         
16757         var buttons = {
16758             tag : 'div',
16759             cls : 'tickable-buttons',
16760             cn : [
16761                 {
16762                     tag : 'button',
16763                     type : 'button',
16764                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16765                     //html : this.triggerText
16766                     html: btn_text_select
16767                 },
16768                 {
16769                     tag : 'button',
16770                     type : 'button',
16771                     name : 'ok',
16772                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16773                     //html : 'Done'
16774                     html: btn_text_done
16775                 },
16776                 {
16777                     tag : 'button',
16778                     type : 'button',
16779                     name : 'cancel',
16780                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16781                     //html : 'Cancel'
16782                     html: btn_text_cancel
16783                 }
16784             ]
16785         };
16786         
16787         if(this.editable){
16788             buttons.cn.unshift({
16789                 tag: 'input',
16790                 cls: 'roo-select2-search-field-input'
16791             });
16792         }
16793         
16794         var _this = this;
16795         
16796         Roo.each(buttons.cn, function(c){
16797             if (_this.size) {
16798                 c.cls += ' btn-' + _this.size;
16799             }
16800
16801             if (_this.disabled) {
16802                 c.disabled = true;
16803             }
16804         });
16805         
16806         var box = {
16807             tag: 'div',
16808             style : 'display: contents',
16809             cn: [
16810                 {
16811                     tag: 'input',
16812                     type : 'hidden',
16813                     cls: 'form-hidden-field'
16814                 },
16815                 {
16816                     tag: 'ul',
16817                     cls: 'roo-select2-choices',
16818                     cn:[
16819                         {
16820                             tag: 'li',
16821                             cls: 'roo-select2-search-field',
16822                             cn: [
16823                                 buttons
16824                             ]
16825                         }
16826                     ]
16827                 }
16828             ]
16829         };
16830         
16831         var combobox = {
16832             cls: 'roo-select2-container input-group roo-select2-container-multi',
16833             cn: [
16834                 
16835                 box
16836 //                {
16837 //                    tag: 'ul',
16838 //                    cls: 'typeahead typeahead-long dropdown-menu',
16839 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16840 //                }
16841             ]
16842         };
16843         
16844         if(this.hasFeedback && !this.allowBlank){
16845             
16846             var feedback = {
16847                 tag: 'span',
16848                 cls: 'glyphicon form-control-feedback'
16849             };
16850
16851             combobox.cn.push(feedback);
16852         }
16853         
16854         
16855         
16856         var indicator = {
16857             tag : 'i',
16858             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16859             tooltip : 'This field is required'
16860         };
16861         if (Roo.bootstrap.version == 4) {
16862             indicator = {
16863                 tag : 'i',
16864                 style : 'display:none'
16865             };
16866         }
16867         if (align ==='left' && this.fieldLabel.length) {
16868             
16869             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16870             
16871             cfg.cn = [
16872                 indicator,
16873                 {
16874                     tag: 'label',
16875                     'for' :  id,
16876                     cls : 'control-label col-form-label',
16877                     html : this.fieldLabel
16878
16879                 },
16880                 {
16881                     cls : "", 
16882                     cn: [
16883                         combobox
16884                     ]
16885                 }
16886
16887             ];
16888             
16889             var labelCfg = cfg.cn[1];
16890             var contentCfg = cfg.cn[2];
16891             
16892
16893             if(this.indicatorpos == 'right'){
16894                 
16895                 cfg.cn = [
16896                     {
16897                         tag: 'label',
16898                         'for' :  id,
16899                         cls : 'control-label col-form-label',
16900                         cn : [
16901                             {
16902                                 tag : 'span',
16903                                 html : this.fieldLabel
16904                             },
16905                             indicator
16906                         ]
16907                     },
16908                     {
16909                         cls : "",
16910                         cn: [
16911                             combobox
16912                         ]
16913                     }
16914
16915                 ];
16916                 
16917                 
16918                 
16919                 labelCfg = cfg.cn[0];
16920                 contentCfg = cfg.cn[1];
16921             
16922             }
16923             
16924             if(this.labelWidth > 12){
16925                 labelCfg.style = "width: " + this.labelWidth + 'px';
16926             }
16927             if(this.width * 1 > 0){
16928                 contentCfg.style = "width: " + this.width + 'px';
16929             }
16930             if(this.labelWidth < 13 && this.labelmd == 0){
16931                 this.labelmd = this.labelWidth;
16932             }
16933             
16934             if(this.labellg > 0){
16935                 labelCfg.cls += ' col-lg-' + this.labellg;
16936                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16937             }
16938             
16939             if(this.labelmd > 0){
16940                 labelCfg.cls += ' col-md-' + this.labelmd;
16941                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16942             }
16943             
16944             if(this.labelsm > 0){
16945                 labelCfg.cls += ' col-sm-' + this.labelsm;
16946                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16947             }
16948             
16949             if(this.labelxs > 0){
16950                 labelCfg.cls += ' col-xs-' + this.labelxs;
16951                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16952             }
16953                 
16954                 
16955         } else if ( this.fieldLabel.length) {
16956 //                Roo.log(" label");
16957                  cfg.cn = [
16958                    indicator,
16959                     {
16960                         tag: 'label',
16961                         //cls : 'input-group-addon',
16962                         html : this.fieldLabel
16963                     },
16964                     combobox
16965                 ];
16966                 
16967                 if(this.indicatorpos == 'right'){
16968                     cfg.cn = [
16969                         {
16970                             tag: 'label',
16971                             //cls : 'input-group-addon',
16972                             html : this.fieldLabel
16973                         },
16974                         indicator,
16975                         combobox
16976                     ];
16977                     
16978                 }
16979
16980         } else {
16981             
16982 //                Roo.log(" no label && no align");
16983                 cfg = combobox
16984                      
16985                 
16986         }
16987          
16988         var settings=this;
16989         ['xs','sm','md','lg'].map(function(size){
16990             if (settings[size]) {
16991                 cfg.cls += ' col-' + size + '-' + settings[size];
16992             }
16993         });
16994         
16995         return cfg;
16996         
16997     },
16998     
16999     _initEventsCalled : false,
17000     
17001     // private
17002     initEvents: function()
17003     {   
17004         if (this._initEventsCalled) { // as we call render... prevent looping...
17005             return;
17006         }
17007         this._initEventsCalled = true;
17008         
17009         if (!this.store) {
17010             throw "can not find store for combo";
17011         }
17012         
17013         this.indicator = this.indicatorEl();
17014         
17015         this.store = Roo.factory(this.store, Roo.data);
17016         this.store.parent = this;
17017         
17018         // if we are building from html. then this element is so complex, that we can not really
17019         // use the rendered HTML.
17020         // so we have to trash and replace the previous code.
17021         if (Roo.XComponent.build_from_html) {
17022             // remove this element....
17023             var e = this.el.dom, k=0;
17024             while (e ) { e = e.previousSibling;  ++k;}
17025
17026             this.el.remove();
17027             
17028             this.el=false;
17029             this.rendered = false;
17030             
17031             this.render(this.parent().getChildContainer(true), k);
17032         }
17033         
17034         if(Roo.isIOS && this.useNativeIOS){
17035             this.initIOSView();
17036             return;
17037         }
17038         
17039         /*
17040          * Touch Devices
17041          */
17042         
17043         if(Roo.isTouch && this.mobileTouchView){
17044             this.initTouchView();
17045             return;
17046         }
17047         
17048         if(this.tickable){
17049             this.initTickableEvents();
17050             return;
17051         }
17052         
17053         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17054         
17055         if(this.hiddenName){
17056             
17057             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17058             
17059             this.hiddenField.dom.value =
17060                 this.hiddenValue !== undefined ? this.hiddenValue :
17061                 this.value !== undefined ? this.value : '';
17062
17063             // prevent input submission
17064             this.el.dom.removeAttribute('name');
17065             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17066              
17067              
17068         }
17069         //if(Roo.isGecko){
17070         //    this.el.dom.setAttribute('autocomplete', 'off');
17071         //}
17072         
17073         var cls = 'x-combo-list';
17074         
17075         //this.list = new Roo.Layer({
17076         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17077         //});
17078         
17079         var _this = this;
17080         
17081         (function(){
17082             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17083             _this.list.setWidth(lw);
17084         }).defer(100);
17085         
17086         this.list.on('mouseover', this.onViewOver, this);
17087         this.list.on('mousemove', this.onViewMove, this);
17088         this.list.on('scroll', this.onViewScroll, this);
17089         
17090         /*
17091         this.list.swallowEvent('mousewheel');
17092         this.assetHeight = 0;
17093
17094         if(this.title){
17095             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17096             this.assetHeight += this.header.getHeight();
17097         }
17098
17099         this.innerList = this.list.createChild({cls:cls+'-inner'});
17100         this.innerList.on('mouseover', this.onViewOver, this);
17101         this.innerList.on('mousemove', this.onViewMove, this);
17102         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17103         
17104         if(this.allowBlank && !this.pageSize && !this.disableClear){
17105             this.footer = this.list.createChild({cls:cls+'-ft'});
17106             this.pageTb = new Roo.Toolbar(this.footer);
17107            
17108         }
17109         if(this.pageSize){
17110             this.footer = this.list.createChild({cls:cls+'-ft'});
17111             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17112                     {pageSize: this.pageSize});
17113             
17114         }
17115         
17116         if (this.pageTb && this.allowBlank && !this.disableClear) {
17117             var _this = this;
17118             this.pageTb.add(new Roo.Toolbar.Fill(), {
17119                 cls: 'x-btn-icon x-btn-clear',
17120                 text: '&#160;',
17121                 handler: function()
17122                 {
17123                     _this.collapse();
17124                     _this.clearValue();
17125                     _this.onSelect(false, -1);
17126                 }
17127             });
17128         }
17129         if (this.footer) {
17130             this.assetHeight += this.footer.getHeight();
17131         }
17132         */
17133             
17134         if(!this.tpl){
17135             this.tpl = Roo.bootstrap.version == 4 ?
17136                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17137                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17138         }
17139
17140         this.view = new Roo.View(this.list, this.tpl, {
17141             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17142         });
17143         //this.view.wrapEl.setDisplayed(false);
17144         this.view.on('click', this.onViewClick, this);
17145         
17146         
17147         this.store.on('beforeload', this.onBeforeLoad, this);
17148         this.store.on('load', this.onLoad, this);
17149         this.store.on('loadexception', this.onLoadException, this);
17150         /*
17151         if(this.resizable){
17152             this.resizer = new Roo.Resizable(this.list,  {
17153                pinned:true, handles:'se'
17154             });
17155             this.resizer.on('resize', function(r, w, h){
17156                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17157                 this.listWidth = w;
17158                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17159                 this.restrictHeight();
17160             }, this);
17161             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17162         }
17163         */
17164         if(!this.editable){
17165             this.editable = true;
17166             this.setEditable(false);
17167         }
17168         
17169         /*
17170         
17171         if (typeof(this.events.add.listeners) != 'undefined') {
17172             
17173             this.addicon = this.wrap.createChild(
17174                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17175        
17176             this.addicon.on('click', function(e) {
17177                 this.fireEvent('add', this);
17178             }, this);
17179         }
17180         if (typeof(this.events.edit.listeners) != 'undefined') {
17181             
17182             this.editicon = this.wrap.createChild(
17183                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17184             if (this.addicon) {
17185                 this.editicon.setStyle('margin-left', '40px');
17186             }
17187             this.editicon.on('click', function(e) {
17188                 
17189                 // we fire even  if inothing is selected..
17190                 this.fireEvent('edit', this, this.lastData );
17191                 
17192             }, this);
17193         }
17194         */
17195         
17196         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17197             "up" : function(e){
17198                 this.inKeyMode = true;
17199                 this.selectPrev();
17200             },
17201
17202             "down" : function(e){
17203                 if(!this.isExpanded()){
17204                     this.onTriggerClick();
17205                 }else{
17206                     this.inKeyMode = true;
17207                     this.selectNext();
17208                 }
17209             },
17210
17211             "enter" : function(e){
17212 //                this.onViewClick();
17213                 //return true;
17214                 this.collapse();
17215                 
17216                 if(this.fireEvent("specialkey", this, e)){
17217                     this.onViewClick(false);
17218                 }
17219                 
17220                 return true;
17221             },
17222
17223             "esc" : function(e){
17224                 this.collapse();
17225             },
17226
17227             "tab" : function(e){
17228                 this.collapse();
17229                 
17230                 if(this.fireEvent("specialkey", this, e)){
17231                     this.onViewClick(false);
17232                 }
17233                 
17234                 return true;
17235             },
17236
17237             scope : this,
17238
17239             doRelay : function(foo, bar, hname){
17240                 if(hname == 'down' || this.scope.isExpanded()){
17241                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17242                 }
17243                 return true;
17244             },
17245
17246             forceKeyDown: true
17247         });
17248         
17249         
17250         this.queryDelay = Math.max(this.queryDelay || 10,
17251                 this.mode == 'local' ? 10 : 250);
17252         
17253         
17254         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17255         
17256         if(this.typeAhead){
17257             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17258         }
17259         if(this.editable !== false){
17260             this.inputEl().on("keyup", this.onKeyUp, this);
17261         }
17262         if(this.forceSelection){
17263             this.inputEl().on('blur', this.doForce, this);
17264         }
17265         
17266         if(this.multiple){
17267             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17268             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17269         }
17270     },
17271     
17272     initTickableEvents: function()
17273     {   
17274         this.createList();
17275         
17276         if(this.hiddenName){
17277             
17278             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17279             
17280             this.hiddenField.dom.value =
17281                 this.hiddenValue !== undefined ? this.hiddenValue :
17282                 this.value !== undefined ? this.value : '';
17283
17284             // prevent input submission
17285             this.el.dom.removeAttribute('name');
17286             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17287              
17288              
17289         }
17290         
17291 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17292         
17293         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17294         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17295         if(this.triggerList){
17296             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17297         }
17298          
17299         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17300         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17301         
17302         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17303         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17304         
17305         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17306         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17307         
17308         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17309         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17310         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17311         
17312         this.okBtn.hide();
17313         this.cancelBtn.hide();
17314         
17315         var _this = this;
17316         
17317         (function(){
17318             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17319             _this.list.setWidth(lw);
17320         }).defer(100);
17321         
17322         this.list.on('mouseover', this.onViewOver, this);
17323         this.list.on('mousemove', this.onViewMove, this);
17324         
17325         this.list.on('scroll', this.onViewScroll, this);
17326         
17327         if(!this.tpl){
17328             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17329                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17330         }
17331
17332         this.view = new Roo.View(this.list, this.tpl, {
17333             singleSelect:true,
17334             tickable:true,
17335             parent:this,
17336             store: this.store,
17337             selectedClass: this.selectedClass
17338         });
17339         
17340         //this.view.wrapEl.setDisplayed(false);
17341         this.view.on('click', this.onViewClick, this);
17342         
17343         
17344         
17345         this.store.on('beforeload', this.onBeforeLoad, this);
17346         this.store.on('load', this.onLoad, this);
17347         this.store.on('loadexception', this.onLoadException, this);
17348         
17349         if(this.editable){
17350             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17351                 "up" : function(e){
17352                     this.inKeyMode = true;
17353                     this.selectPrev();
17354                 },
17355
17356                 "down" : function(e){
17357                     this.inKeyMode = true;
17358                     this.selectNext();
17359                 },
17360
17361                 "enter" : function(e){
17362                     if(this.fireEvent("specialkey", this, e)){
17363                         this.onViewClick(false);
17364                     }
17365                     
17366                     return true;
17367                 },
17368
17369                 "esc" : function(e){
17370                     this.onTickableFooterButtonClick(e, false, false);
17371                 },
17372
17373                 "tab" : function(e){
17374                     this.fireEvent("specialkey", this, e);
17375                     
17376                     this.onTickableFooterButtonClick(e, false, false);
17377                     
17378                     return true;
17379                 },
17380
17381                 scope : this,
17382
17383                 doRelay : function(e, fn, key){
17384                     if(this.scope.isExpanded()){
17385                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17386                     }
17387                     return true;
17388                 },
17389
17390                 forceKeyDown: true
17391             });
17392         }
17393         
17394         this.queryDelay = Math.max(this.queryDelay || 10,
17395                 this.mode == 'local' ? 10 : 250);
17396         
17397         
17398         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17399         
17400         if(this.typeAhead){
17401             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17402         }
17403         
17404         if(this.editable !== false){
17405             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17406         }
17407         
17408         this.indicator = this.indicatorEl();
17409         
17410         if(this.indicator){
17411             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17412             this.indicator.hide();
17413         }
17414         
17415     },
17416
17417     onDestroy : function(){
17418         if(this.view){
17419             this.view.setStore(null);
17420             this.view.el.removeAllListeners();
17421             this.view.el.remove();
17422             this.view.purgeListeners();
17423         }
17424         if(this.list){
17425             this.list.dom.innerHTML  = '';
17426         }
17427         
17428         if(this.store){
17429             this.store.un('beforeload', this.onBeforeLoad, this);
17430             this.store.un('load', this.onLoad, this);
17431             this.store.un('loadexception', this.onLoadException, this);
17432         }
17433         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17434     },
17435
17436     // private
17437     fireKey : function(e){
17438         if(e.isNavKeyPress() && !this.list.isVisible()){
17439             this.fireEvent("specialkey", this, e);
17440         }
17441     },
17442
17443     // private
17444     onResize: function(w, h)
17445     {
17446         
17447         
17448 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17449 //        
17450 //        if(typeof w != 'number'){
17451 //            // we do not handle it!?!?
17452 //            return;
17453 //        }
17454 //        var tw = this.trigger.getWidth();
17455 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17456 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17457 //        var x = w - tw;
17458 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17459 //            
17460 //        //this.trigger.setStyle('left', x+'px');
17461 //        
17462 //        if(this.list && this.listWidth === undefined){
17463 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17464 //            this.list.setWidth(lw);
17465 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17466 //        }
17467         
17468     
17469         
17470     },
17471
17472     /**
17473      * Allow or prevent the user from directly editing the field text.  If false is passed,
17474      * the user will only be able to select from the items defined in the dropdown list.  This method
17475      * is the runtime equivalent of setting the 'editable' config option at config time.
17476      * @param {Boolean} value True to allow the user to directly edit the field text
17477      */
17478     setEditable : function(value){
17479         if(value == this.editable){
17480             return;
17481         }
17482         this.editable = value;
17483         if(!value){
17484             this.inputEl().dom.setAttribute('readOnly', true);
17485             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17486             this.inputEl().addClass('x-combo-noedit');
17487         }else{
17488             this.inputEl().dom.removeAttribute('readOnly');
17489             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17490             this.inputEl().removeClass('x-combo-noedit');
17491         }
17492     },
17493
17494     // private
17495     
17496     onBeforeLoad : function(combo,opts){
17497         if(!this.hasFocus){
17498             return;
17499         }
17500          if (!opts.add) {
17501             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17502          }
17503         this.restrictHeight();
17504         this.selectedIndex = -1;
17505     },
17506
17507     // private
17508     onLoad : function(){
17509         
17510         this.hasQuery = false;
17511         
17512         if(!this.hasFocus){
17513             return;
17514         }
17515         
17516         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17517             this.loading.hide();
17518         }
17519         
17520         if(this.store.getCount() > 0){
17521             
17522             this.expand();
17523             this.restrictHeight();
17524             if(this.lastQuery == this.allQuery){
17525                 if(this.editable && !this.tickable){
17526                     this.inputEl().dom.select();
17527                 }
17528                 
17529                 if(
17530                     !this.selectByValue(this.value, true) &&
17531                     this.autoFocus && 
17532                     (
17533                         !this.store.lastOptions ||
17534                         typeof(this.store.lastOptions.add) == 'undefined' || 
17535                         this.store.lastOptions.add != true
17536                     )
17537                 ){
17538                     this.select(0, true);
17539                 }
17540             }else{
17541                 if(this.autoFocus){
17542                     this.selectNext();
17543                 }
17544                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17545                     this.taTask.delay(this.typeAheadDelay);
17546                 }
17547             }
17548         }else{
17549             this.onEmptyResults();
17550         }
17551         
17552         //this.el.focus();
17553     },
17554     // private
17555     onLoadException : function()
17556     {
17557         this.hasQuery = false;
17558         
17559         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17560             this.loading.hide();
17561         }
17562         
17563         if(this.tickable && this.editable){
17564             return;
17565         }
17566         
17567         this.collapse();
17568         // only causes errors at present
17569         //Roo.log(this.store.reader.jsonData);
17570         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17571             // fixme
17572             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17573         //}
17574         
17575         
17576     },
17577     // private
17578     onTypeAhead : function(){
17579         if(this.store.getCount() > 0){
17580             var r = this.store.getAt(0);
17581             var newValue = r.data[this.displayField];
17582             var len = newValue.length;
17583             var selStart = this.getRawValue().length;
17584             
17585             if(selStart != len){
17586                 this.setRawValue(newValue);
17587                 this.selectText(selStart, newValue.length);
17588             }
17589         }
17590     },
17591
17592     // private
17593     onSelect : function(record, index){
17594         
17595         if(this.fireEvent('beforeselect', this, record, index) !== false){
17596         
17597             this.setFromData(index > -1 ? record.data : false);
17598             
17599             this.collapse();
17600             this.fireEvent('select', this, record, index);
17601         }
17602     },
17603
17604     /**
17605      * Returns the currently selected field value or empty string if no value is set.
17606      * @return {String} value The selected value
17607      */
17608     getValue : function()
17609     {
17610         if(Roo.isIOS && this.useNativeIOS){
17611             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17612         }
17613         
17614         if(this.multiple){
17615             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17616         }
17617         
17618         if(this.valueField){
17619             return typeof this.value != 'undefined' ? this.value : '';
17620         }else{
17621             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17622         }
17623     },
17624     
17625     getRawValue : function()
17626     {
17627         if(Roo.isIOS && this.useNativeIOS){
17628             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17629         }
17630         
17631         var v = this.inputEl().getValue();
17632         
17633         return v;
17634     },
17635
17636     /**
17637      * Clears any text/value currently set in the field
17638      */
17639     clearValue : function(){
17640         
17641         if(this.hiddenField){
17642             this.hiddenField.dom.value = '';
17643         }
17644         this.value = '';
17645         this.setRawValue('');
17646         this.lastSelectionText = '';
17647         this.lastData = false;
17648         
17649         var close = this.closeTriggerEl();
17650         
17651         if(close){
17652             close.hide();
17653         }
17654         
17655         this.validate();
17656         
17657     },
17658
17659     /**
17660      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17661      * will be displayed in the field.  If the value does not match the data value of an existing item,
17662      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17663      * Otherwise the field will be blank (although the value will still be set).
17664      * @param {String} value The value to match
17665      */
17666     setValue : function(v)
17667     {
17668         if(Roo.isIOS && this.useNativeIOS){
17669             this.setIOSValue(v);
17670             return;
17671         }
17672         
17673         if(this.multiple){
17674             this.syncValue();
17675             return;
17676         }
17677         
17678         var text = v;
17679         if(this.valueField){
17680             var r = this.findRecord(this.valueField, v);
17681             if(r){
17682                 text = r.data[this.displayField];
17683             }else if(this.valueNotFoundText !== undefined){
17684                 text = this.valueNotFoundText;
17685             }
17686         }
17687         this.lastSelectionText = text;
17688         if(this.hiddenField){
17689             this.hiddenField.dom.value = v;
17690         }
17691         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17692         this.value = v;
17693         
17694         var close = this.closeTriggerEl();
17695         
17696         if(close){
17697             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17698         }
17699         
17700         this.validate();
17701     },
17702     /**
17703      * @property {Object} the last set data for the element
17704      */
17705     
17706     lastData : false,
17707     /**
17708      * Sets the value of the field based on a object which is related to the record format for the store.
17709      * @param {Object} value the value to set as. or false on reset?
17710      */
17711     setFromData : function(o){
17712         
17713         if(this.multiple){
17714             this.addItem(o);
17715             return;
17716         }
17717             
17718         var dv = ''; // display value
17719         var vv = ''; // value value..
17720         this.lastData = o;
17721         if (this.displayField) {
17722             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17723         } else {
17724             // this is an error condition!!!
17725             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17726         }
17727         
17728         if(this.valueField){
17729             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17730         }
17731         
17732         var close = this.closeTriggerEl();
17733         
17734         if(close){
17735             if(dv.length || vv * 1 > 0){
17736                 close.show() ;
17737                 this.blockFocus=true;
17738             } else {
17739                 close.hide();
17740             }             
17741         }
17742         
17743         if(this.hiddenField){
17744             this.hiddenField.dom.value = vv;
17745             
17746             this.lastSelectionText = dv;
17747             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17748             this.value = vv;
17749             return;
17750         }
17751         // no hidden field.. - we store the value in 'value', but still display
17752         // display field!!!!
17753         this.lastSelectionText = dv;
17754         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17755         this.value = vv;
17756         
17757         
17758         
17759     },
17760     // private
17761     reset : function(){
17762         // overridden so that last data is reset..
17763         
17764         if(this.multiple){
17765             this.clearItem();
17766             return;
17767         }
17768         
17769         this.setValue(this.originalValue);
17770         //this.clearInvalid();
17771         this.lastData = false;
17772         if (this.view) {
17773             this.view.clearSelections();
17774         }
17775         
17776         this.validate();
17777     },
17778     // private
17779     findRecord : function(prop, value){
17780         var record;
17781         if(this.store.getCount() > 0){
17782             this.store.each(function(r){
17783                 if(r.data[prop] == value){
17784                     record = r;
17785                     return false;
17786                 }
17787                 return true;
17788             });
17789         }
17790         return record;
17791     },
17792     
17793     getName: function()
17794     {
17795         // returns hidden if it's set..
17796         if (!this.rendered) {return ''};
17797         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17798         
17799     },
17800     // private
17801     onViewMove : function(e, t){
17802         this.inKeyMode = false;
17803     },
17804
17805     // private
17806     onViewOver : function(e, t){
17807         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17808             return;
17809         }
17810         var item = this.view.findItemFromChild(t);
17811         
17812         if(item){
17813             var index = this.view.indexOf(item);
17814             this.select(index, false);
17815         }
17816     },
17817
17818     // private
17819     onViewClick : function(view, doFocus, el, e)
17820     {
17821         var index = this.view.getSelectedIndexes()[0];
17822         
17823         var r = this.store.getAt(index);
17824         
17825         if(this.tickable){
17826             
17827             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17828                 return;
17829             }
17830             
17831             var rm = false;
17832             var _this = this;
17833             
17834             Roo.each(this.tickItems, function(v,k){
17835                 
17836                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17837                     Roo.log(v);
17838                     _this.tickItems.splice(k, 1);
17839                     
17840                     if(typeof(e) == 'undefined' && view == false){
17841                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17842                     }
17843                     
17844                     rm = true;
17845                     return;
17846                 }
17847             });
17848             
17849             if(rm){
17850                 return;
17851             }
17852             
17853             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17854                 this.tickItems.push(r.data);
17855             }
17856             
17857             if(typeof(e) == 'undefined' && view == false){
17858                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17859             }
17860                     
17861             return;
17862         }
17863         
17864         if(r){
17865             this.onSelect(r, index);
17866         }
17867         if(doFocus !== false && !this.blockFocus){
17868             this.inputEl().focus();
17869         }
17870     },
17871
17872     // private
17873     restrictHeight : function(){
17874         //this.innerList.dom.style.height = '';
17875         //var inner = this.innerList.dom;
17876         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17877         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17878         //this.list.beginUpdate();
17879         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17880         this.list.alignTo(this.inputEl(), this.listAlign);
17881         this.list.alignTo(this.inputEl(), this.listAlign);
17882         //this.list.endUpdate();
17883     },
17884
17885     // private
17886     onEmptyResults : function(){
17887         
17888         if(this.tickable && this.editable){
17889             this.hasFocus = false;
17890             this.restrictHeight();
17891             return;
17892         }
17893         
17894         this.collapse();
17895     },
17896
17897     /**
17898      * Returns true if the dropdown list is expanded, else false.
17899      */
17900     isExpanded : function(){
17901         return this.list.isVisible();
17902     },
17903
17904     /**
17905      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17906      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17907      * @param {String} value The data value of the item to select
17908      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17909      * selected item if it is not currently in view (defaults to true)
17910      * @return {Boolean} True if the value matched an item in the list, else false
17911      */
17912     selectByValue : function(v, scrollIntoView){
17913         if(v !== undefined && v !== null){
17914             var r = this.findRecord(this.valueField || this.displayField, v);
17915             if(r){
17916                 this.select(this.store.indexOf(r), scrollIntoView);
17917                 return true;
17918             }
17919         }
17920         return false;
17921     },
17922
17923     /**
17924      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17925      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17926      * @param {Number} index The zero-based index of the list item to select
17927      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17928      * selected item if it is not currently in view (defaults to true)
17929      */
17930     select : function(index, scrollIntoView){
17931         this.selectedIndex = index;
17932         this.view.select(index);
17933         if(scrollIntoView !== false){
17934             var el = this.view.getNode(index);
17935             /*
17936              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17937              */
17938             if(el){
17939                 this.list.scrollChildIntoView(el, false);
17940             }
17941         }
17942     },
17943
17944     // private
17945     selectNext : function(){
17946         var ct = this.store.getCount();
17947         if(ct > 0){
17948             if(this.selectedIndex == -1){
17949                 this.select(0);
17950             }else if(this.selectedIndex < ct-1){
17951                 this.select(this.selectedIndex+1);
17952             }
17953         }
17954     },
17955
17956     // private
17957     selectPrev : function(){
17958         var ct = this.store.getCount();
17959         if(ct > 0){
17960             if(this.selectedIndex == -1){
17961                 this.select(0);
17962             }else if(this.selectedIndex != 0){
17963                 this.select(this.selectedIndex-1);
17964             }
17965         }
17966     },
17967
17968     // private
17969     onKeyUp : function(e){
17970         if(this.editable !== false && !e.isSpecialKey()){
17971             this.lastKey = e.getKey();
17972             this.dqTask.delay(this.queryDelay);
17973         }
17974     },
17975
17976     // private
17977     validateBlur : function(){
17978         return !this.list || !this.list.isVisible();   
17979     },
17980
17981     // private
17982     initQuery : function(){
17983         
17984         var v = this.getRawValue();
17985         
17986         if(this.tickable && this.editable){
17987             v = this.tickableInputEl().getValue();
17988         }
17989         
17990         this.doQuery(v);
17991     },
17992
17993     // private
17994     doForce : function(){
17995         if(this.inputEl().dom.value.length > 0){
17996             this.inputEl().dom.value =
17997                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17998              
17999         }
18000     },
18001
18002     /**
18003      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18004      * query allowing the query action to be canceled if needed.
18005      * @param {String} query The SQL query to execute
18006      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18007      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18008      * saved in the current store (defaults to false)
18009      */
18010     doQuery : function(q, forceAll){
18011         
18012         if(q === undefined || q === null){
18013             q = '';
18014         }
18015         var qe = {
18016             query: q,
18017             forceAll: forceAll,
18018             combo: this,
18019             cancel:false
18020         };
18021         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18022             return false;
18023         }
18024         q = qe.query;
18025         
18026         forceAll = qe.forceAll;
18027         if(forceAll === true || (q.length >= this.minChars)){
18028             
18029             this.hasQuery = true;
18030             
18031             if(this.lastQuery != q || this.alwaysQuery){
18032                 this.lastQuery = q;
18033                 if(this.mode == 'local'){
18034                     this.selectedIndex = -1;
18035                     if(forceAll){
18036                         this.store.clearFilter();
18037                     }else{
18038                         
18039                         if(this.specialFilter){
18040                             this.fireEvent('specialfilter', this);
18041                             this.onLoad();
18042                             return;
18043                         }
18044                         
18045                         this.store.filter(this.displayField, q);
18046                     }
18047                     
18048                     this.store.fireEvent("datachanged", this.store);
18049                     
18050                     this.onLoad();
18051                     
18052                     
18053                 }else{
18054                     
18055                     this.store.baseParams[this.queryParam] = q;
18056                     
18057                     var options = {params : this.getParams(q)};
18058                     
18059                     if(this.loadNext){
18060                         options.add = true;
18061                         options.params.start = this.page * this.pageSize;
18062                     }
18063                     
18064                     this.store.load(options);
18065                     
18066                     /*
18067                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18068                      *  we should expand the list on onLoad
18069                      *  so command out it
18070                      */
18071 //                    this.expand();
18072                 }
18073             }else{
18074                 this.selectedIndex = -1;
18075                 this.onLoad();   
18076             }
18077         }
18078         
18079         this.loadNext = false;
18080     },
18081     
18082     // private
18083     getParams : function(q){
18084         var p = {};
18085         //p[this.queryParam] = q;
18086         
18087         if(this.pageSize){
18088             p.start = 0;
18089             p.limit = this.pageSize;
18090         }
18091         return p;
18092     },
18093
18094     /**
18095      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18096      */
18097     collapse : function(){
18098         if(!this.isExpanded()){
18099             return;
18100         }
18101         
18102         this.list.hide();
18103         
18104         this.hasFocus = false;
18105         
18106         if(this.tickable){
18107             this.okBtn.hide();
18108             this.cancelBtn.hide();
18109             this.trigger.show();
18110             
18111             if(this.editable){
18112                 this.tickableInputEl().dom.value = '';
18113                 this.tickableInputEl().blur();
18114             }
18115             
18116         }
18117         
18118         Roo.get(document).un('mousedown', this.collapseIf, this);
18119         Roo.get(document).un('mousewheel', this.collapseIf, this);
18120         if (!this.editable) {
18121             Roo.get(document).un('keydown', this.listKeyPress, this);
18122         }
18123         this.fireEvent('collapse', this);
18124         
18125         this.validate();
18126     },
18127
18128     // private
18129     collapseIf : function(e){
18130         var in_combo  = e.within(this.el);
18131         var in_list =  e.within(this.list);
18132         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18133         
18134         if (in_combo || in_list || is_list) {
18135             //e.stopPropagation();
18136             return;
18137         }
18138         
18139         if(this.tickable){
18140             this.onTickableFooterButtonClick(e, false, false);
18141         }
18142
18143         this.collapse();
18144         
18145     },
18146
18147     /**
18148      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18149      */
18150     expand : function(){
18151        
18152         if(this.isExpanded() || !this.hasFocus){
18153             return;
18154         }
18155         
18156         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18157         this.list.setWidth(lw);
18158         
18159         Roo.log('expand');
18160         
18161         this.list.show();
18162         
18163         this.restrictHeight();
18164         
18165         if(this.tickable){
18166             
18167             this.tickItems = Roo.apply([], this.item);
18168             
18169             this.okBtn.show();
18170             this.cancelBtn.show();
18171             this.trigger.hide();
18172             
18173             if(this.editable){
18174                 this.tickableInputEl().focus();
18175             }
18176             
18177         }
18178         
18179         Roo.get(document).on('mousedown', this.collapseIf, this);
18180         Roo.get(document).on('mousewheel', this.collapseIf, this);
18181         if (!this.editable) {
18182             Roo.get(document).on('keydown', this.listKeyPress, this);
18183         }
18184         
18185         this.fireEvent('expand', this);
18186     },
18187
18188     // private
18189     // Implements the default empty TriggerField.onTriggerClick function
18190     onTriggerClick : function(e)
18191     {
18192         Roo.log('trigger click');
18193         
18194         if(this.disabled || !this.triggerList){
18195             return;
18196         }
18197         
18198         this.page = 0;
18199         this.loadNext = false;
18200         
18201         if(this.isExpanded()){
18202             this.collapse();
18203             if (!this.blockFocus) {
18204                 this.inputEl().focus();
18205             }
18206             
18207         }else {
18208             this.hasFocus = true;
18209             if(this.triggerAction == 'all') {
18210                 this.doQuery(this.allQuery, true);
18211             } else {
18212                 this.doQuery(this.getRawValue());
18213             }
18214             if (!this.blockFocus) {
18215                 this.inputEl().focus();
18216             }
18217         }
18218     },
18219     
18220     onTickableTriggerClick : function(e)
18221     {
18222         if(this.disabled){
18223             return;
18224         }
18225         
18226         this.page = 0;
18227         this.loadNext = false;
18228         this.hasFocus = true;
18229         
18230         if(this.triggerAction == 'all') {
18231             this.doQuery(this.allQuery, true);
18232         } else {
18233             this.doQuery(this.getRawValue());
18234         }
18235     },
18236     
18237     onSearchFieldClick : function(e)
18238     {
18239         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18240             this.onTickableFooterButtonClick(e, false, false);
18241             return;
18242         }
18243         
18244         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18245             return;
18246         }
18247         
18248         this.page = 0;
18249         this.loadNext = false;
18250         this.hasFocus = true;
18251         
18252         if(this.triggerAction == 'all') {
18253             this.doQuery(this.allQuery, true);
18254         } else {
18255             this.doQuery(this.getRawValue());
18256         }
18257     },
18258     
18259     listKeyPress : function(e)
18260     {
18261         //Roo.log('listkeypress');
18262         // scroll to first matching element based on key pres..
18263         if (e.isSpecialKey()) {
18264             return false;
18265         }
18266         var k = String.fromCharCode(e.getKey()).toUpperCase();
18267         //Roo.log(k);
18268         var match  = false;
18269         var csel = this.view.getSelectedNodes();
18270         var cselitem = false;
18271         if (csel.length) {
18272             var ix = this.view.indexOf(csel[0]);
18273             cselitem  = this.store.getAt(ix);
18274             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18275                 cselitem = false;
18276             }
18277             
18278         }
18279         
18280         this.store.each(function(v) { 
18281             if (cselitem) {
18282                 // start at existing selection.
18283                 if (cselitem.id == v.id) {
18284                     cselitem = false;
18285                 }
18286                 return true;
18287             }
18288                 
18289             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18290                 match = this.store.indexOf(v);
18291                 return false;
18292             }
18293             return true;
18294         }, this);
18295         
18296         if (match === false) {
18297             return true; // no more action?
18298         }
18299         // scroll to?
18300         this.view.select(match);
18301         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18302         sn.scrollIntoView(sn.dom.parentNode, false);
18303     },
18304     
18305     onViewScroll : function(e, t){
18306         
18307         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){
18308             return;
18309         }
18310         
18311         this.hasQuery = true;
18312         
18313         this.loading = this.list.select('.loading', true).first();
18314         
18315         if(this.loading === null){
18316             this.list.createChild({
18317                 tag: 'div',
18318                 cls: 'loading roo-select2-more-results roo-select2-active',
18319                 html: 'Loading more results...'
18320             });
18321             
18322             this.loading = this.list.select('.loading', true).first();
18323             
18324             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18325             
18326             this.loading.hide();
18327         }
18328         
18329         this.loading.show();
18330         
18331         var _combo = this;
18332         
18333         this.page++;
18334         this.loadNext = true;
18335         
18336         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18337         
18338         return;
18339     },
18340     
18341     addItem : function(o)
18342     {   
18343         var dv = ''; // display value
18344         
18345         if (this.displayField) {
18346             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18347         } else {
18348             // this is an error condition!!!
18349             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18350         }
18351         
18352         if(!dv.length){
18353             return;
18354         }
18355         
18356         var choice = this.choices.createChild({
18357             tag: 'li',
18358             cls: 'roo-select2-search-choice',
18359             cn: [
18360                 {
18361                     tag: 'div',
18362                     html: dv
18363                 },
18364                 {
18365                     tag: 'a',
18366                     href: '#',
18367                     cls: 'roo-select2-search-choice-close fa fa-times',
18368                     tabindex: '-1'
18369                 }
18370             ]
18371             
18372         }, this.searchField);
18373         
18374         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18375         
18376         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18377         
18378         this.item.push(o);
18379         
18380         this.lastData = o;
18381         
18382         this.syncValue();
18383         
18384         this.inputEl().dom.value = '';
18385         
18386         this.validate();
18387     },
18388     
18389     onRemoveItem : function(e, _self, o)
18390     {
18391         e.preventDefault();
18392         
18393         this.lastItem = Roo.apply([], this.item);
18394         
18395         var index = this.item.indexOf(o.data) * 1;
18396         
18397         if( index < 0){
18398             Roo.log('not this item?!');
18399             return;
18400         }
18401         
18402         this.item.splice(index, 1);
18403         o.item.remove();
18404         
18405         this.syncValue();
18406         
18407         this.fireEvent('remove', this, e);
18408         
18409         this.validate();
18410         
18411     },
18412     
18413     syncValue : function()
18414     {
18415         if(!this.item.length){
18416             this.clearValue();
18417             return;
18418         }
18419             
18420         var value = [];
18421         var _this = this;
18422         Roo.each(this.item, function(i){
18423             if(_this.valueField){
18424                 value.push(i[_this.valueField]);
18425                 return;
18426             }
18427
18428             value.push(i);
18429         });
18430
18431         this.value = value.join(',');
18432
18433         if(this.hiddenField){
18434             this.hiddenField.dom.value = this.value;
18435         }
18436         
18437         this.store.fireEvent("datachanged", this.store);
18438         
18439         this.validate();
18440     },
18441     
18442     clearItem : function()
18443     {
18444         if(!this.multiple){
18445             return;
18446         }
18447         
18448         this.item = [];
18449         
18450         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18451            c.remove();
18452         });
18453         
18454         this.syncValue();
18455         
18456         this.validate();
18457         
18458         if(this.tickable && !Roo.isTouch){
18459             this.view.refresh();
18460         }
18461     },
18462     
18463     inputEl: function ()
18464     {
18465         if(Roo.isIOS && this.useNativeIOS){
18466             return this.el.select('select.roo-ios-select', true).first();
18467         }
18468         
18469         if(Roo.isTouch && this.mobileTouchView){
18470             return this.el.select('input.form-control',true).first();
18471         }
18472         
18473         if(this.tickable){
18474             return this.searchField;
18475         }
18476         
18477         return this.el.select('input.form-control',true).first();
18478     },
18479     
18480     onTickableFooterButtonClick : function(e, btn, el)
18481     {
18482         e.preventDefault();
18483         
18484         this.lastItem = Roo.apply([], this.item);
18485         
18486         if(btn && btn.name == 'cancel'){
18487             this.tickItems = Roo.apply([], this.item);
18488             this.collapse();
18489             return;
18490         }
18491         
18492         this.clearItem();
18493         
18494         var _this = this;
18495         
18496         Roo.each(this.tickItems, function(o){
18497             _this.addItem(o);
18498         });
18499         
18500         this.collapse();
18501         
18502     },
18503     
18504     validate : function()
18505     {
18506         if(this.getVisibilityEl().hasClass('hidden')){
18507             return true;
18508         }
18509         
18510         var v = this.getRawValue();
18511         
18512         if(this.multiple){
18513             v = this.getValue();
18514         }
18515         
18516         if(this.disabled || this.allowBlank || v.length){
18517             this.markValid();
18518             return true;
18519         }
18520         
18521         this.markInvalid();
18522         return false;
18523     },
18524     
18525     tickableInputEl : function()
18526     {
18527         if(!this.tickable || !this.editable){
18528             return this.inputEl();
18529         }
18530         
18531         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18532     },
18533     
18534     
18535     getAutoCreateTouchView : function()
18536     {
18537         var id = Roo.id();
18538         
18539         var cfg = {
18540             cls: 'form-group' //input-group
18541         };
18542         
18543         var input =  {
18544             tag: 'input',
18545             id : id,
18546             type : this.inputType,
18547             cls : 'form-control x-combo-noedit',
18548             autocomplete: 'new-password',
18549             placeholder : this.placeholder || '',
18550             readonly : true
18551         };
18552         
18553         if (this.name) {
18554             input.name = this.name;
18555         }
18556         
18557         if (this.size) {
18558             input.cls += ' input-' + this.size;
18559         }
18560         
18561         if (this.disabled) {
18562             input.disabled = true;
18563         }
18564         
18565         var inputblock = {
18566             cls : 'roo-combobox-wrap',
18567             cn : [
18568                 input
18569             ]
18570         };
18571         
18572         if(this.before){
18573             inputblock.cls += ' input-group';
18574             
18575             inputblock.cn.unshift({
18576                 tag :'span',
18577                 cls : 'input-group-addon input-group-prepend input-group-text',
18578                 html : this.before
18579             });
18580         }
18581         
18582         if(this.removable && !this.multiple){
18583             inputblock.cls += ' roo-removable';
18584             
18585             inputblock.cn.push({
18586                 tag: 'button',
18587                 html : 'x',
18588                 cls : 'roo-combo-removable-btn close'
18589             });
18590         }
18591
18592         if(this.hasFeedback && !this.allowBlank){
18593             
18594             inputblock.cls += ' has-feedback';
18595             
18596             inputblock.cn.push({
18597                 tag: 'span',
18598                 cls: 'glyphicon form-control-feedback'
18599             });
18600             
18601         }
18602         
18603         if (this.after) {
18604             
18605             inputblock.cls += (this.before) ? '' : ' input-group';
18606             
18607             inputblock.cn.push({
18608                 tag :'span',
18609                 cls : 'input-group-addon input-group-append input-group-text',
18610                 html : this.after
18611             });
18612         }
18613
18614         
18615         var ibwrap = inputblock;
18616         
18617         if(this.multiple){
18618             ibwrap = {
18619                 tag: 'ul',
18620                 cls: 'roo-select2-choices',
18621                 cn:[
18622                     {
18623                         tag: 'li',
18624                         cls: 'roo-select2-search-field',
18625                         cn: [
18626
18627                             inputblock
18628                         ]
18629                     }
18630                 ]
18631             };
18632         
18633             
18634         }
18635         
18636         var combobox = {
18637             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18638             cn: [
18639                 {
18640                     tag: 'input',
18641                     type : 'hidden',
18642                     cls: 'form-hidden-field'
18643                 },
18644                 ibwrap
18645             ]
18646         };
18647         
18648         if(!this.multiple && this.showToggleBtn){
18649             
18650             var caret = {
18651                 cls: 'caret'
18652             };
18653             
18654             if (this.caret != false) {
18655                 caret = {
18656                      tag: 'i',
18657                      cls: 'fa fa-' + this.caret
18658                 };
18659                 
18660             }
18661             
18662             combobox.cn.push({
18663                 tag :'span',
18664                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18665                 cn : [
18666                     Roo.bootstrap.version == 3 ? caret : '',
18667                     {
18668                         tag: 'span',
18669                         cls: 'combobox-clear',
18670                         cn  : [
18671                             {
18672                                 tag : 'i',
18673                                 cls: 'icon-remove'
18674                             }
18675                         ]
18676                     }
18677                 ]
18678
18679             })
18680         }
18681         
18682         if(this.multiple){
18683             combobox.cls += ' roo-select2-container-multi';
18684         }
18685         
18686         var required =  this.allowBlank ?  {
18687                     tag : 'i',
18688                     style: 'display: none'
18689                 } : {
18690                    tag : 'i',
18691                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18692                    tooltip : 'This field is required'
18693                 };
18694         
18695         var align = this.labelAlign || this.parentLabelAlign();
18696         
18697         if (align ==='left' && this.fieldLabel.length) {
18698
18699             cfg.cn = [
18700                 required,
18701                 {
18702                     tag: 'label',
18703                     cls : 'control-label col-form-label',
18704                     html : this.fieldLabel
18705
18706                 },
18707                 {
18708                     cls : 'roo-combobox-wrap ', 
18709                     cn: [
18710                         combobox
18711                     ]
18712                 }
18713             ];
18714             
18715             var labelCfg = cfg.cn[1];
18716             var contentCfg = cfg.cn[2];
18717             
18718
18719             if(this.indicatorpos == 'right'){
18720                 cfg.cn = [
18721                     {
18722                         tag: 'label',
18723                         'for' :  id,
18724                         cls : 'control-label col-form-label',
18725                         cn : [
18726                             {
18727                                 tag : 'span',
18728                                 html : this.fieldLabel
18729                             },
18730                             required
18731                         ]
18732                     },
18733                     {
18734                         cls : "roo-combobox-wrap ",
18735                         cn: [
18736                             combobox
18737                         ]
18738                     }
18739
18740                 ];
18741                 
18742                 labelCfg = cfg.cn[0];
18743                 contentCfg = cfg.cn[1];
18744             }
18745             
18746            
18747             
18748             if(this.labelWidth > 12){
18749                 labelCfg.style = "width: " + this.labelWidth + 'px';
18750             }
18751            
18752             if(this.labelWidth < 13 && this.labelmd == 0){
18753                 this.labelmd = this.labelWidth;
18754             }
18755             
18756             if(this.labellg > 0){
18757                 labelCfg.cls += ' col-lg-' + this.labellg;
18758                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18759             }
18760             
18761             if(this.labelmd > 0){
18762                 labelCfg.cls += ' col-md-' + this.labelmd;
18763                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18764             }
18765             
18766             if(this.labelsm > 0){
18767                 labelCfg.cls += ' col-sm-' + this.labelsm;
18768                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18769             }
18770             
18771             if(this.labelxs > 0){
18772                 labelCfg.cls += ' col-xs-' + this.labelxs;
18773                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18774             }
18775                 
18776                 
18777         } else if ( this.fieldLabel.length) {
18778             cfg.cn = [
18779                required,
18780                 {
18781                     tag: 'label',
18782                     cls : 'control-label',
18783                     html : this.fieldLabel
18784
18785                 },
18786                 {
18787                     cls : '', 
18788                     cn: [
18789                         combobox
18790                     ]
18791                 }
18792             ];
18793             
18794             if(this.indicatorpos == 'right'){
18795                 cfg.cn = [
18796                     {
18797                         tag: 'label',
18798                         cls : 'control-label',
18799                         html : this.fieldLabel,
18800                         cn : [
18801                             required
18802                         ]
18803                     },
18804                     {
18805                         cls : '', 
18806                         cn: [
18807                             combobox
18808                         ]
18809                     }
18810                 ];
18811             }
18812         } else {
18813             cfg.cn = combobox;    
18814         }
18815         
18816         
18817         var settings = this;
18818         
18819         ['xs','sm','md','lg'].map(function(size){
18820             if (settings[size]) {
18821                 cfg.cls += ' col-' + size + '-' + settings[size];
18822             }
18823         });
18824         
18825         return cfg;
18826     },
18827     
18828     initTouchView : function()
18829     {
18830         this.renderTouchView();
18831         
18832         this.touchViewEl.on('scroll', function(){
18833             this.el.dom.scrollTop = 0;
18834         }, this);
18835         
18836         this.originalValue = this.getValue();
18837         
18838         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18839         
18840         this.inputEl().on("click", this.showTouchView, this);
18841         if (this.triggerEl) {
18842             this.triggerEl.on("click", this.showTouchView, this);
18843         }
18844         
18845         
18846         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18847         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18848         
18849         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18850         
18851         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18852         this.store.on('load', this.onTouchViewLoad, this);
18853         this.store.on('loadexception', this.onTouchViewLoadException, this);
18854         
18855         if(this.hiddenName){
18856             
18857             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18858             
18859             this.hiddenField.dom.value =
18860                 this.hiddenValue !== undefined ? this.hiddenValue :
18861                 this.value !== undefined ? this.value : '';
18862         
18863             this.el.dom.removeAttribute('name');
18864             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18865         }
18866         
18867         if(this.multiple){
18868             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18869             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18870         }
18871         
18872         if(this.removable && !this.multiple){
18873             var close = this.closeTriggerEl();
18874             if(close){
18875                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18876                 close.on('click', this.removeBtnClick, this, close);
18877             }
18878         }
18879         /*
18880          * fix the bug in Safari iOS8
18881          */
18882         this.inputEl().on("focus", function(e){
18883             document.activeElement.blur();
18884         }, this);
18885         
18886         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18887         
18888         return;
18889         
18890         
18891     },
18892     
18893     renderTouchView : function()
18894     {
18895         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18896         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18897         
18898         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18899         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18900         
18901         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18902         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18903         this.touchViewBodyEl.setStyle('overflow', 'auto');
18904         
18905         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18906         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18907         
18908         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18909         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18910         
18911     },
18912     
18913     showTouchView : function()
18914     {
18915         if(this.disabled){
18916             return;
18917         }
18918         
18919         this.touchViewHeaderEl.hide();
18920
18921         if(this.modalTitle.length){
18922             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18923             this.touchViewHeaderEl.show();
18924         }
18925
18926         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18927         this.touchViewEl.show();
18928
18929         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18930         
18931         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18932         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18933
18934         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18935
18936         if(this.modalTitle.length){
18937             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18938         }
18939         
18940         this.touchViewBodyEl.setHeight(bodyHeight);
18941
18942         if(this.animate){
18943             var _this = this;
18944             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18945         }else{
18946             this.touchViewEl.addClass(['in','show']);
18947         }
18948         
18949         if(this._touchViewMask){
18950             Roo.get(document.body).addClass("x-body-masked");
18951             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18952             this._touchViewMask.setStyle('z-index', 10000);
18953             this._touchViewMask.addClass('show');
18954         }
18955         
18956         this.doTouchViewQuery();
18957         
18958     },
18959     
18960     hideTouchView : function()
18961     {
18962         this.touchViewEl.removeClass(['in','show']);
18963
18964         if(this.animate){
18965             var _this = this;
18966             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18967         }else{
18968             this.touchViewEl.setStyle('display', 'none');
18969         }
18970         
18971         if(this._touchViewMask){
18972             this._touchViewMask.removeClass('show');
18973             Roo.get(document.body).removeClass("x-body-masked");
18974         }
18975     },
18976     
18977     setTouchViewValue : function()
18978     {
18979         if(this.multiple){
18980             this.clearItem();
18981         
18982             var _this = this;
18983
18984             Roo.each(this.tickItems, function(o){
18985                 this.addItem(o);
18986             }, this);
18987         }
18988         
18989         this.hideTouchView();
18990     },
18991     
18992     doTouchViewQuery : function()
18993     {
18994         var qe = {
18995             query: '',
18996             forceAll: true,
18997             combo: this,
18998             cancel:false
18999         };
19000         
19001         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19002             return false;
19003         }
19004         
19005         if(!this.alwaysQuery || this.mode == 'local'){
19006             this.onTouchViewLoad();
19007             return;
19008         }
19009         
19010         this.store.load();
19011     },
19012     
19013     onTouchViewBeforeLoad : function(combo,opts)
19014     {
19015         return;
19016     },
19017
19018     // private
19019     onTouchViewLoad : function()
19020     {
19021         if(this.store.getCount() < 1){
19022             this.onTouchViewEmptyResults();
19023             return;
19024         }
19025         
19026         this.clearTouchView();
19027         
19028         var rawValue = this.getRawValue();
19029         
19030         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19031         
19032         this.tickItems = [];
19033         
19034         this.store.data.each(function(d, rowIndex){
19035             var row = this.touchViewListGroup.createChild(template);
19036             
19037             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19038                 row.addClass(d.data.cls);
19039             }
19040             
19041             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19042                 var cfg = {
19043                     data : d.data,
19044                     html : d.data[this.displayField]
19045                 };
19046                 
19047                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19048                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19049                 }
19050             }
19051             row.removeClass('selected');
19052             if(!this.multiple && this.valueField &&
19053                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19054             {
19055                 // radio buttons..
19056                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19057                 row.addClass('selected');
19058             }
19059             
19060             if(this.multiple && this.valueField &&
19061                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19062             {
19063                 
19064                 // checkboxes...
19065                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19066                 this.tickItems.push(d.data);
19067             }
19068             
19069             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19070             
19071         }, this);
19072         
19073         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19074         
19075         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19076
19077         if(this.modalTitle.length){
19078             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19079         }
19080
19081         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19082         
19083         if(this.mobile_restrict_height && listHeight < bodyHeight){
19084             this.touchViewBodyEl.setHeight(listHeight);
19085         }
19086         
19087         var _this = this;
19088         
19089         if(firstChecked && listHeight > bodyHeight){
19090             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19091         }
19092         
19093     },
19094     
19095     onTouchViewLoadException : function()
19096     {
19097         this.hideTouchView();
19098     },
19099     
19100     onTouchViewEmptyResults : function()
19101     {
19102         this.clearTouchView();
19103         
19104         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19105         
19106         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19107         
19108     },
19109     
19110     clearTouchView : function()
19111     {
19112         this.touchViewListGroup.dom.innerHTML = '';
19113     },
19114     
19115     onTouchViewClick : function(e, el, o)
19116     {
19117         e.preventDefault();
19118         
19119         var row = o.row;
19120         var rowIndex = o.rowIndex;
19121         
19122         var r = this.store.getAt(rowIndex);
19123         
19124         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19125             
19126             if(!this.multiple){
19127                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19128                     c.dom.removeAttribute('checked');
19129                 }, this);
19130
19131                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19132
19133                 this.setFromData(r.data);
19134
19135                 var close = this.closeTriggerEl();
19136
19137                 if(close){
19138                     close.show();
19139                 }
19140
19141                 this.hideTouchView();
19142
19143                 this.fireEvent('select', this, r, rowIndex);
19144
19145                 return;
19146             }
19147
19148             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19149                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19150                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19151                 return;
19152             }
19153
19154             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19155             this.addItem(r.data);
19156             this.tickItems.push(r.data);
19157         }
19158     },
19159     
19160     getAutoCreateNativeIOS : function()
19161     {
19162         var cfg = {
19163             cls: 'form-group' //input-group,
19164         };
19165         
19166         var combobox =  {
19167             tag: 'select',
19168             cls : 'roo-ios-select'
19169         };
19170         
19171         if (this.name) {
19172             combobox.name = this.name;
19173         }
19174         
19175         if (this.disabled) {
19176             combobox.disabled = true;
19177         }
19178         
19179         var settings = this;
19180         
19181         ['xs','sm','md','lg'].map(function(size){
19182             if (settings[size]) {
19183                 cfg.cls += ' col-' + size + '-' + settings[size];
19184             }
19185         });
19186         
19187         cfg.cn = combobox;
19188         
19189         return cfg;
19190         
19191     },
19192     
19193     initIOSView : function()
19194     {
19195         this.store.on('load', this.onIOSViewLoad, this);
19196         
19197         return;
19198     },
19199     
19200     onIOSViewLoad : function()
19201     {
19202         if(this.store.getCount() < 1){
19203             return;
19204         }
19205         
19206         this.clearIOSView();
19207         
19208         if(this.allowBlank) {
19209             
19210             var default_text = '-- SELECT --';
19211             
19212             if(this.placeholder.length){
19213                 default_text = this.placeholder;
19214             }
19215             
19216             if(this.emptyTitle.length){
19217                 default_text += ' - ' + this.emptyTitle + ' -';
19218             }
19219             
19220             var opt = this.inputEl().createChild({
19221                 tag: 'option',
19222                 value : 0,
19223                 html : default_text
19224             });
19225             
19226             var o = {};
19227             o[this.valueField] = 0;
19228             o[this.displayField] = default_text;
19229             
19230             this.ios_options.push({
19231                 data : o,
19232                 el : opt
19233             });
19234             
19235         }
19236         
19237         this.store.data.each(function(d, rowIndex){
19238             
19239             var html = '';
19240             
19241             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19242                 html = d.data[this.displayField];
19243             }
19244             
19245             var value = '';
19246             
19247             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19248                 value = d.data[this.valueField];
19249             }
19250             
19251             var option = {
19252                 tag: 'option',
19253                 value : value,
19254                 html : html
19255             };
19256             
19257             if(this.value == d.data[this.valueField]){
19258                 option['selected'] = true;
19259             }
19260             
19261             var opt = this.inputEl().createChild(option);
19262             
19263             this.ios_options.push({
19264                 data : d.data,
19265                 el : opt
19266             });
19267             
19268         }, this);
19269         
19270         this.inputEl().on('change', function(){
19271            this.fireEvent('select', this);
19272         }, this);
19273         
19274     },
19275     
19276     clearIOSView: function()
19277     {
19278         this.inputEl().dom.innerHTML = '';
19279         
19280         this.ios_options = [];
19281     },
19282     
19283     setIOSValue: function(v)
19284     {
19285         this.value = v;
19286         
19287         if(!this.ios_options){
19288             return;
19289         }
19290         
19291         Roo.each(this.ios_options, function(opts){
19292            
19293            opts.el.dom.removeAttribute('selected');
19294            
19295            if(opts.data[this.valueField] != v){
19296                return;
19297            }
19298            
19299            opts.el.dom.setAttribute('selected', true);
19300            
19301         }, this);
19302     }
19303
19304     /** 
19305     * @cfg {Boolean} grow 
19306     * @hide 
19307     */
19308     /** 
19309     * @cfg {Number} growMin 
19310     * @hide 
19311     */
19312     /** 
19313     * @cfg {Number} growMax 
19314     * @hide 
19315     */
19316     /**
19317      * @hide
19318      * @method autoSize
19319      */
19320 });
19321
19322 Roo.apply(Roo.bootstrap.ComboBox,  {
19323     
19324     header : {
19325         tag: 'div',
19326         cls: 'modal-header',
19327         cn: [
19328             {
19329                 tag: 'h4',
19330                 cls: 'modal-title'
19331             }
19332         ]
19333     },
19334     
19335     body : {
19336         tag: 'div',
19337         cls: 'modal-body',
19338         cn: [
19339             {
19340                 tag: 'ul',
19341                 cls: 'list-group'
19342             }
19343         ]
19344     },
19345     
19346     listItemRadio : {
19347         tag: 'li',
19348         cls: 'list-group-item',
19349         cn: [
19350             {
19351                 tag: 'span',
19352                 cls: 'roo-combobox-list-group-item-value'
19353             },
19354             {
19355                 tag: 'div',
19356                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19357                 cn: [
19358                     {
19359                         tag: 'input',
19360                         type: 'radio'
19361                     },
19362                     {
19363                         tag: 'label'
19364                     }
19365                 ]
19366             }
19367         ]
19368     },
19369     
19370     listItemCheckbox : {
19371         tag: 'li',
19372         cls: 'list-group-item',
19373         cn: [
19374             {
19375                 tag: 'span',
19376                 cls: 'roo-combobox-list-group-item-value'
19377             },
19378             {
19379                 tag: 'div',
19380                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19381                 cn: [
19382                     {
19383                         tag: 'input',
19384                         type: 'checkbox'
19385                     },
19386                     {
19387                         tag: 'label'
19388                     }
19389                 ]
19390             }
19391         ]
19392     },
19393     
19394     emptyResult : {
19395         tag: 'div',
19396         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19397     },
19398     
19399     footer : {
19400         tag: 'div',
19401         cls: 'modal-footer',
19402         cn: [
19403             {
19404                 tag: 'div',
19405                 cls: 'row',
19406                 cn: [
19407                     {
19408                         tag: 'div',
19409                         cls: 'col-xs-6 text-left',
19410                         cn: {
19411                             tag: 'button',
19412                             cls: 'btn btn-danger roo-touch-view-cancel',
19413                             html: 'Cancel'
19414                         }
19415                     },
19416                     {
19417                         tag: 'div',
19418                         cls: 'col-xs-6 text-right',
19419                         cn: {
19420                             tag: 'button',
19421                             cls: 'btn btn-success roo-touch-view-ok',
19422                             html: 'OK'
19423                         }
19424                     }
19425                 ]
19426             }
19427         ]
19428         
19429     }
19430 });
19431
19432 Roo.apply(Roo.bootstrap.ComboBox,  {
19433     
19434     touchViewTemplate : {
19435         tag: 'div',
19436         cls: 'modal fade roo-combobox-touch-view',
19437         cn: [
19438             {
19439                 tag: 'div',
19440                 cls: 'modal-dialog',
19441                 style : 'position:fixed', // we have to fix position....
19442                 cn: [
19443                     {
19444                         tag: 'div',
19445                         cls: 'modal-content',
19446                         cn: [
19447                             Roo.bootstrap.ComboBox.header,
19448                             Roo.bootstrap.ComboBox.body,
19449                             Roo.bootstrap.ComboBox.footer
19450                         ]
19451                     }
19452                 ]
19453             }
19454         ]
19455     }
19456 });/*
19457  * Based on:
19458  * Ext JS Library 1.1.1
19459  * Copyright(c) 2006-2007, Ext JS, LLC.
19460  *
19461  * Originally Released Under LGPL - original licence link has changed is not relivant.
19462  *
19463  * Fork - LGPL
19464  * <script type="text/javascript">
19465  */
19466
19467 /**
19468  * @class Roo.View
19469  * @extends Roo.util.Observable
19470  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19471  * This class also supports single and multi selection modes. <br>
19472  * Create a data model bound view:
19473  <pre><code>
19474  var store = new Roo.data.Store(...);
19475
19476  var view = new Roo.View({
19477     el : "my-element",
19478     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19479  
19480     singleSelect: true,
19481     selectedClass: "ydataview-selected",
19482     store: store
19483  });
19484
19485  // listen for node click?
19486  view.on("click", function(vw, index, node, e){
19487  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19488  });
19489
19490  // load XML data
19491  dataModel.load("foobar.xml");
19492  </code></pre>
19493  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19494  * <br><br>
19495  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19496  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19497  * 
19498  * Note: old style constructor is still suported (container, template, config)
19499  * 
19500  * @constructor
19501  * Create a new View
19502  * @param {Object} config The config object
19503  * 
19504  */
19505 Roo.View = function(config, depreciated_tpl, depreciated_config){
19506     
19507     this.parent = false;
19508     
19509     if (typeof(depreciated_tpl) == 'undefined') {
19510         // new way.. - universal constructor.
19511         Roo.apply(this, config);
19512         this.el  = Roo.get(this.el);
19513     } else {
19514         // old format..
19515         this.el  = Roo.get(config);
19516         this.tpl = depreciated_tpl;
19517         Roo.apply(this, depreciated_config);
19518     }
19519     this.wrapEl  = this.el.wrap().wrap();
19520     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19521     
19522     
19523     if(typeof(this.tpl) == "string"){
19524         this.tpl = new Roo.Template(this.tpl);
19525     } else {
19526         // support xtype ctors..
19527         this.tpl = new Roo.factory(this.tpl, Roo);
19528     }
19529     
19530     
19531     this.tpl.compile();
19532     
19533     /** @private */
19534     this.addEvents({
19535         /**
19536          * @event beforeclick
19537          * Fires before a click is processed. Returns false to cancel the default action.
19538          * @param {Roo.View} this
19539          * @param {Number} index The index of the target node
19540          * @param {HTMLElement} node The target node
19541          * @param {Roo.EventObject} e The raw event object
19542          */
19543             "beforeclick" : true,
19544         /**
19545          * @event click
19546          * Fires when a template node is clicked.
19547          * @param {Roo.View} this
19548          * @param {Number} index The index of the target node
19549          * @param {HTMLElement} node The target node
19550          * @param {Roo.EventObject} e The raw event object
19551          */
19552             "click" : true,
19553         /**
19554          * @event dblclick
19555          * Fires when a template node is double clicked.
19556          * @param {Roo.View} this
19557          * @param {Number} index The index of the target node
19558          * @param {HTMLElement} node The target node
19559          * @param {Roo.EventObject} e The raw event object
19560          */
19561             "dblclick" : true,
19562         /**
19563          * @event contextmenu
19564          * Fires when a template node is right clicked.
19565          * @param {Roo.View} this
19566          * @param {Number} index The index of the target node
19567          * @param {HTMLElement} node The target node
19568          * @param {Roo.EventObject} e The raw event object
19569          */
19570             "contextmenu" : true,
19571         /**
19572          * @event selectionchange
19573          * Fires when the selected nodes change.
19574          * @param {Roo.View} this
19575          * @param {Array} selections Array of the selected nodes
19576          */
19577             "selectionchange" : true,
19578     
19579         /**
19580          * @event beforeselect
19581          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19582          * @param {Roo.View} this
19583          * @param {HTMLElement} node The node to be selected
19584          * @param {Array} selections Array of currently selected nodes
19585          */
19586             "beforeselect" : true,
19587         /**
19588          * @event preparedata
19589          * Fires on every row to render, to allow you to change the data.
19590          * @param {Roo.View} this
19591          * @param {Object} data to be rendered (change this)
19592          */
19593           "preparedata" : true
19594           
19595           
19596         });
19597
19598
19599
19600     this.el.on({
19601         "click": this.onClick,
19602         "dblclick": this.onDblClick,
19603         "contextmenu": this.onContextMenu,
19604         scope:this
19605     });
19606
19607     this.selections = [];
19608     this.nodes = [];
19609     this.cmp = new Roo.CompositeElementLite([]);
19610     if(this.store){
19611         this.store = Roo.factory(this.store, Roo.data);
19612         this.setStore(this.store, true);
19613     }
19614     
19615     if ( this.footer && this.footer.xtype) {
19616            
19617          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19618         
19619         this.footer.dataSource = this.store;
19620         this.footer.container = fctr;
19621         this.footer = Roo.factory(this.footer, Roo);
19622         fctr.insertFirst(this.el);
19623         
19624         // this is a bit insane - as the paging toolbar seems to detach the el..
19625 //        dom.parentNode.parentNode.parentNode
19626          // they get detached?
19627     }
19628     
19629     
19630     Roo.View.superclass.constructor.call(this);
19631     
19632     
19633 };
19634
19635 Roo.extend(Roo.View, Roo.util.Observable, {
19636     
19637      /**
19638      * @cfg {Roo.data.Store} store Data store to load data from.
19639      */
19640     store : false,
19641     
19642     /**
19643      * @cfg {String|Roo.Element} el The container element.
19644      */
19645     el : '',
19646     
19647     /**
19648      * @cfg {String|Roo.Template} tpl The template used by this View 
19649      */
19650     tpl : false,
19651     /**
19652      * @cfg {String} dataName the named area of the template to use as the data area
19653      *                          Works with domtemplates roo-name="name"
19654      */
19655     dataName: false,
19656     /**
19657      * @cfg {String} selectedClass The css class to add to selected nodes
19658      */
19659     selectedClass : "x-view-selected",
19660      /**
19661      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19662      */
19663     emptyText : "",
19664     
19665     /**
19666      * @cfg {String} text to display on mask (default Loading)
19667      */
19668     mask : false,
19669     /**
19670      * @cfg {Boolean} multiSelect Allow multiple selection
19671      */
19672     multiSelect : false,
19673     /**
19674      * @cfg {Boolean} singleSelect Allow single selection
19675      */
19676     singleSelect:  false,
19677     
19678     /**
19679      * @cfg {Boolean} toggleSelect - selecting 
19680      */
19681     toggleSelect : false,
19682     
19683     /**
19684      * @cfg {Boolean} tickable - selecting 
19685      */
19686     tickable : false,
19687     
19688     /**
19689      * Returns the element this view is bound to.
19690      * @return {Roo.Element}
19691      */
19692     getEl : function(){
19693         return this.wrapEl;
19694     },
19695     
19696     
19697
19698     /**
19699      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19700      */
19701     refresh : function(){
19702         //Roo.log('refresh');
19703         var t = this.tpl;
19704         
19705         // if we are using something like 'domtemplate', then
19706         // the what gets used is:
19707         // t.applySubtemplate(NAME, data, wrapping data..)
19708         // the outer template then get' applied with
19709         //     the store 'extra data'
19710         // and the body get's added to the
19711         //      roo-name="data" node?
19712         //      <span class='roo-tpl-{name}'></span> ?????
19713         
19714         
19715         
19716         this.clearSelections();
19717         this.el.update("");
19718         var html = [];
19719         var records = this.store.getRange();
19720         if(records.length < 1) {
19721             
19722             // is this valid??  = should it render a template??
19723             
19724             this.el.update(this.emptyText);
19725             return;
19726         }
19727         var el = this.el;
19728         if (this.dataName) {
19729             this.el.update(t.apply(this.store.meta)); //????
19730             el = this.el.child('.roo-tpl-' + this.dataName);
19731         }
19732         
19733         for(var i = 0, len = records.length; i < len; i++){
19734             var data = this.prepareData(records[i].data, i, records[i]);
19735             this.fireEvent("preparedata", this, data, i, records[i]);
19736             
19737             var d = Roo.apply({}, data);
19738             
19739             if(this.tickable){
19740                 Roo.apply(d, {'roo-id' : Roo.id()});
19741                 
19742                 var _this = this;
19743             
19744                 Roo.each(this.parent.item, function(item){
19745                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19746                         return;
19747                     }
19748                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19749                 });
19750             }
19751             
19752             html[html.length] = Roo.util.Format.trim(
19753                 this.dataName ?
19754                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19755                     t.apply(d)
19756             );
19757         }
19758         
19759         
19760         
19761         el.update(html.join(""));
19762         this.nodes = el.dom.childNodes;
19763         this.updateIndexes(0);
19764     },
19765     
19766
19767     /**
19768      * Function to override to reformat the data that is sent to
19769      * the template for each node.
19770      * DEPRICATED - use the preparedata event handler.
19771      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19772      * a JSON object for an UpdateManager bound view).
19773      */
19774     prepareData : function(data, index, record)
19775     {
19776         this.fireEvent("preparedata", this, data, index, record);
19777         return data;
19778     },
19779
19780     onUpdate : function(ds, record){
19781         // Roo.log('on update');   
19782         this.clearSelections();
19783         var index = this.store.indexOf(record);
19784         var n = this.nodes[index];
19785         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19786         n.parentNode.removeChild(n);
19787         this.updateIndexes(index, index);
19788     },
19789
19790     
19791     
19792 // --------- FIXME     
19793     onAdd : function(ds, records, index)
19794     {
19795         //Roo.log(['on Add', ds, records, index] );        
19796         this.clearSelections();
19797         if(this.nodes.length == 0){
19798             this.refresh();
19799             return;
19800         }
19801         var n = this.nodes[index];
19802         for(var i = 0, len = records.length; i < len; i++){
19803             var d = this.prepareData(records[i].data, i, records[i]);
19804             if(n){
19805                 this.tpl.insertBefore(n, d);
19806             }else{
19807                 
19808                 this.tpl.append(this.el, d);
19809             }
19810         }
19811         this.updateIndexes(index);
19812     },
19813
19814     onRemove : function(ds, record, index){
19815        // Roo.log('onRemove');
19816         this.clearSelections();
19817         var el = this.dataName  ?
19818             this.el.child('.roo-tpl-' + this.dataName) :
19819             this.el; 
19820         
19821         el.dom.removeChild(this.nodes[index]);
19822         this.updateIndexes(index);
19823     },
19824
19825     /**
19826      * Refresh an individual node.
19827      * @param {Number} index
19828      */
19829     refreshNode : function(index){
19830         this.onUpdate(this.store, this.store.getAt(index));
19831     },
19832
19833     updateIndexes : function(startIndex, endIndex){
19834         var ns = this.nodes;
19835         startIndex = startIndex || 0;
19836         endIndex = endIndex || ns.length - 1;
19837         for(var i = startIndex; i <= endIndex; i++){
19838             ns[i].nodeIndex = i;
19839         }
19840     },
19841
19842     /**
19843      * Changes the data store this view uses and refresh the view.
19844      * @param {Store} store
19845      */
19846     setStore : function(store, initial){
19847         if(!initial && this.store){
19848             this.store.un("datachanged", this.refresh);
19849             this.store.un("add", this.onAdd);
19850             this.store.un("remove", this.onRemove);
19851             this.store.un("update", this.onUpdate);
19852             this.store.un("clear", this.refresh);
19853             this.store.un("beforeload", this.onBeforeLoad);
19854             this.store.un("load", this.onLoad);
19855             this.store.un("loadexception", this.onLoad);
19856         }
19857         if(store){
19858           
19859             store.on("datachanged", this.refresh, this);
19860             store.on("add", this.onAdd, this);
19861             store.on("remove", this.onRemove, this);
19862             store.on("update", this.onUpdate, this);
19863             store.on("clear", this.refresh, this);
19864             store.on("beforeload", this.onBeforeLoad, this);
19865             store.on("load", this.onLoad, this);
19866             store.on("loadexception", this.onLoad, this);
19867         }
19868         
19869         if(store){
19870             this.refresh();
19871         }
19872     },
19873     /**
19874      * onbeforeLoad - masks the loading area.
19875      *
19876      */
19877     onBeforeLoad : function(store,opts)
19878     {
19879          //Roo.log('onBeforeLoad');   
19880         if (!opts.add) {
19881             this.el.update("");
19882         }
19883         this.el.mask(this.mask ? this.mask : "Loading" ); 
19884     },
19885     onLoad : function ()
19886     {
19887         this.el.unmask();
19888     },
19889     
19890
19891     /**
19892      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19893      * @param {HTMLElement} node
19894      * @return {HTMLElement} The template node
19895      */
19896     findItemFromChild : function(node){
19897         var el = this.dataName  ?
19898             this.el.child('.roo-tpl-' + this.dataName,true) :
19899             this.el.dom; 
19900         
19901         if(!node || node.parentNode == el){
19902                     return node;
19903             }
19904             var p = node.parentNode;
19905             while(p && p != el){
19906             if(p.parentNode == el){
19907                 return p;
19908             }
19909             p = p.parentNode;
19910         }
19911             return null;
19912     },
19913
19914     /** @ignore */
19915     onClick : function(e){
19916         var item = this.findItemFromChild(e.getTarget());
19917         if(item){
19918             var index = this.indexOf(item);
19919             if(this.onItemClick(item, index, e) !== false){
19920                 this.fireEvent("click", this, index, item, e);
19921             }
19922         }else{
19923             this.clearSelections();
19924         }
19925     },
19926
19927     /** @ignore */
19928     onContextMenu : function(e){
19929         var item = this.findItemFromChild(e.getTarget());
19930         if(item){
19931             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19932         }
19933     },
19934
19935     /** @ignore */
19936     onDblClick : function(e){
19937         var item = this.findItemFromChild(e.getTarget());
19938         if(item){
19939             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19940         }
19941     },
19942
19943     onItemClick : function(item, index, e)
19944     {
19945         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19946             return false;
19947         }
19948         if (this.toggleSelect) {
19949             var m = this.isSelected(item) ? 'unselect' : 'select';
19950             //Roo.log(m);
19951             var _t = this;
19952             _t[m](item, true, false);
19953             return true;
19954         }
19955         if(this.multiSelect || this.singleSelect){
19956             if(this.multiSelect && e.shiftKey && this.lastSelection){
19957                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19958             }else{
19959                 this.select(item, this.multiSelect && e.ctrlKey);
19960                 this.lastSelection = item;
19961             }
19962             
19963             if(!this.tickable){
19964                 e.preventDefault();
19965             }
19966             
19967         }
19968         return true;
19969     },
19970
19971     /**
19972      * Get the number of selected nodes.
19973      * @return {Number}
19974      */
19975     getSelectionCount : function(){
19976         return this.selections.length;
19977     },
19978
19979     /**
19980      * Get the currently selected nodes.
19981      * @return {Array} An array of HTMLElements
19982      */
19983     getSelectedNodes : function(){
19984         return this.selections;
19985     },
19986
19987     /**
19988      * Get the indexes of the selected nodes.
19989      * @return {Array}
19990      */
19991     getSelectedIndexes : function(){
19992         var indexes = [], s = this.selections;
19993         for(var i = 0, len = s.length; i < len; i++){
19994             indexes.push(s[i].nodeIndex);
19995         }
19996         return indexes;
19997     },
19998
19999     /**
20000      * Clear all selections
20001      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20002      */
20003     clearSelections : function(suppressEvent){
20004         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20005             this.cmp.elements = this.selections;
20006             this.cmp.removeClass(this.selectedClass);
20007             this.selections = [];
20008             if(!suppressEvent){
20009                 this.fireEvent("selectionchange", this, this.selections);
20010             }
20011         }
20012     },
20013
20014     /**
20015      * Returns true if the passed node is selected
20016      * @param {HTMLElement/Number} node The node or node index
20017      * @return {Boolean}
20018      */
20019     isSelected : function(node){
20020         var s = this.selections;
20021         if(s.length < 1){
20022             return false;
20023         }
20024         node = this.getNode(node);
20025         return s.indexOf(node) !== -1;
20026     },
20027
20028     /**
20029      * Selects nodes.
20030      * @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
20031      * @param {Boolean} keepExisting (optional) true to keep existing selections
20032      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20033      */
20034     select : function(nodeInfo, keepExisting, suppressEvent){
20035         if(nodeInfo instanceof Array){
20036             if(!keepExisting){
20037                 this.clearSelections(true);
20038             }
20039             for(var i = 0, len = nodeInfo.length; i < len; i++){
20040                 this.select(nodeInfo[i], true, true);
20041             }
20042             return;
20043         } 
20044         var node = this.getNode(nodeInfo);
20045         if(!node || this.isSelected(node)){
20046             return; // already selected.
20047         }
20048         if(!keepExisting){
20049             this.clearSelections(true);
20050         }
20051         
20052         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20053             Roo.fly(node).addClass(this.selectedClass);
20054             this.selections.push(node);
20055             if(!suppressEvent){
20056                 this.fireEvent("selectionchange", this, this.selections);
20057             }
20058         }
20059         
20060         
20061     },
20062       /**
20063      * Unselects nodes.
20064      * @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
20065      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20066      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20067      */
20068     unselect : function(nodeInfo, keepExisting, suppressEvent)
20069     {
20070         if(nodeInfo instanceof Array){
20071             Roo.each(this.selections, function(s) {
20072                 this.unselect(s, nodeInfo);
20073             }, this);
20074             return;
20075         }
20076         var node = this.getNode(nodeInfo);
20077         if(!node || !this.isSelected(node)){
20078             //Roo.log("not selected");
20079             return; // not selected.
20080         }
20081         // fireevent???
20082         var ns = [];
20083         Roo.each(this.selections, function(s) {
20084             if (s == node ) {
20085                 Roo.fly(node).removeClass(this.selectedClass);
20086
20087                 return;
20088             }
20089             ns.push(s);
20090         },this);
20091         
20092         this.selections= ns;
20093         this.fireEvent("selectionchange", this, this.selections);
20094     },
20095
20096     /**
20097      * Gets a template node.
20098      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20099      * @return {HTMLElement} The node or null if it wasn't found
20100      */
20101     getNode : function(nodeInfo){
20102         if(typeof nodeInfo == "string"){
20103             return document.getElementById(nodeInfo);
20104         }else if(typeof nodeInfo == "number"){
20105             return this.nodes[nodeInfo];
20106         }
20107         return nodeInfo;
20108     },
20109
20110     /**
20111      * Gets a range template nodes.
20112      * @param {Number} startIndex
20113      * @param {Number} endIndex
20114      * @return {Array} An array of nodes
20115      */
20116     getNodes : function(start, end){
20117         var ns = this.nodes;
20118         start = start || 0;
20119         end = typeof end == "undefined" ? ns.length - 1 : end;
20120         var nodes = [];
20121         if(start <= end){
20122             for(var i = start; i <= end; i++){
20123                 nodes.push(ns[i]);
20124             }
20125         } else{
20126             for(var i = start; i >= end; i--){
20127                 nodes.push(ns[i]);
20128             }
20129         }
20130         return nodes;
20131     },
20132
20133     /**
20134      * Finds the index of the passed node
20135      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20136      * @return {Number} The index of the node or -1
20137      */
20138     indexOf : function(node){
20139         node = this.getNode(node);
20140         if(typeof node.nodeIndex == "number"){
20141             return node.nodeIndex;
20142         }
20143         var ns = this.nodes;
20144         for(var i = 0, len = ns.length; i < len; i++){
20145             if(ns[i] == node){
20146                 return i;
20147             }
20148         }
20149         return -1;
20150     }
20151 });
20152 /*
20153  * - LGPL
20154  *
20155  * based on jquery fullcalendar
20156  * 
20157  */
20158
20159 Roo.bootstrap = Roo.bootstrap || {};
20160 /**
20161  * @class Roo.bootstrap.Calendar
20162  * @extends Roo.bootstrap.Component
20163  * Bootstrap Calendar class
20164  * @cfg {Boolean} loadMask (true|false) default false
20165  * @cfg {Object} header generate the user specific header of the calendar, default false
20166
20167  * @constructor
20168  * Create a new Container
20169  * @param {Object} config The config object
20170  */
20171
20172
20173
20174 Roo.bootstrap.Calendar = function(config){
20175     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20176      this.addEvents({
20177         /**
20178              * @event select
20179              * Fires when a date is selected
20180              * @param {DatePicker} this
20181              * @param {Date} date The selected date
20182              */
20183         'select': true,
20184         /**
20185              * @event monthchange
20186              * Fires when the displayed month changes 
20187              * @param {DatePicker} this
20188              * @param {Date} date The selected month
20189              */
20190         'monthchange': true,
20191         /**
20192              * @event evententer
20193              * Fires when mouse over an event
20194              * @param {Calendar} this
20195              * @param {event} Event
20196              */
20197         'evententer': true,
20198         /**
20199              * @event eventleave
20200              * Fires when the mouse leaves an
20201              * @param {Calendar} this
20202              * @param {event}
20203              */
20204         'eventleave': true,
20205         /**
20206              * @event eventclick
20207              * Fires when the mouse click an
20208              * @param {Calendar} this
20209              * @param {event}
20210              */
20211         'eventclick': true
20212         
20213     });
20214
20215 };
20216
20217 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20218     
20219           /**
20220      * @cfg {Roo.data.Store} store
20221      * The data source for the calendar
20222      */
20223         store : false,
20224      /**
20225      * @cfg {Number} startDay
20226      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20227      */
20228     startDay : 0,
20229     
20230     loadMask : false,
20231     
20232     header : false,
20233       
20234     getAutoCreate : function(){
20235         
20236         
20237         var fc_button = function(name, corner, style, content ) {
20238             return Roo.apply({},{
20239                 tag : 'span',
20240                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20241                          (corner.length ?
20242                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20243                             ''
20244                         ),
20245                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20246                 unselectable: 'on'
20247             });
20248         };
20249         
20250         var header = {};
20251         
20252         if(!this.header){
20253             header = {
20254                 tag : 'table',
20255                 cls : 'fc-header',
20256                 style : 'width:100%',
20257                 cn : [
20258                     {
20259                         tag: 'tr',
20260                         cn : [
20261                             {
20262                                 tag : 'td',
20263                                 cls : 'fc-header-left',
20264                                 cn : [
20265                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20266                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20267                                     { tag: 'span', cls: 'fc-header-space' },
20268                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20269
20270
20271                                 ]
20272                             },
20273
20274                             {
20275                                 tag : 'td',
20276                                 cls : 'fc-header-center',
20277                                 cn : [
20278                                     {
20279                                         tag: 'span',
20280                                         cls: 'fc-header-title',
20281                                         cn : {
20282                                             tag: 'H2',
20283                                             html : 'month / year'
20284                                         }
20285                                     }
20286
20287                                 ]
20288                             },
20289                             {
20290                                 tag : 'td',
20291                                 cls : 'fc-header-right',
20292                                 cn : [
20293                               /*      fc_button('month', 'left', '', 'month' ),
20294                                     fc_button('week', '', '', 'week' ),
20295                                     fc_button('day', 'right', '', 'day' )
20296                                 */    
20297
20298                                 ]
20299                             }
20300
20301                         ]
20302                     }
20303                 ]
20304             };
20305         }
20306         
20307         header = this.header;
20308         
20309        
20310         var cal_heads = function() {
20311             var ret = [];
20312             // fixme - handle this.
20313             
20314             for (var i =0; i < Date.dayNames.length; i++) {
20315                 var d = Date.dayNames[i];
20316                 ret.push({
20317                     tag: 'th',
20318                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20319                     html : d.substring(0,3)
20320                 });
20321                 
20322             }
20323             ret[0].cls += ' fc-first';
20324             ret[6].cls += ' fc-last';
20325             return ret;
20326         };
20327         var cal_cell = function(n) {
20328             return  {
20329                 tag: 'td',
20330                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20331                 cn : [
20332                     {
20333                         cn : [
20334                             {
20335                                 cls: 'fc-day-number',
20336                                 html: 'D'
20337                             },
20338                             {
20339                                 cls: 'fc-day-content',
20340                              
20341                                 cn : [
20342                                      {
20343                                         style: 'position: relative;' // height: 17px;
20344                                     }
20345                                 ]
20346                             }
20347                             
20348                             
20349                         ]
20350                     }
20351                 ]
20352                 
20353             }
20354         };
20355         var cal_rows = function() {
20356             
20357             var ret = [];
20358             for (var r = 0; r < 6; r++) {
20359                 var row= {
20360                     tag : 'tr',
20361                     cls : 'fc-week',
20362                     cn : []
20363                 };
20364                 
20365                 for (var i =0; i < Date.dayNames.length; i++) {
20366                     var d = Date.dayNames[i];
20367                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20368
20369                 }
20370                 row.cn[0].cls+=' fc-first';
20371                 row.cn[0].cn[0].style = 'min-height:90px';
20372                 row.cn[6].cls+=' fc-last';
20373                 ret.push(row);
20374                 
20375             }
20376             ret[0].cls += ' fc-first';
20377             ret[4].cls += ' fc-prev-last';
20378             ret[5].cls += ' fc-last';
20379             return ret;
20380             
20381         };
20382         
20383         var cal_table = {
20384             tag: 'table',
20385             cls: 'fc-border-separate',
20386             style : 'width:100%',
20387             cellspacing  : 0,
20388             cn : [
20389                 { 
20390                     tag: 'thead',
20391                     cn : [
20392                         { 
20393                             tag: 'tr',
20394                             cls : 'fc-first fc-last',
20395                             cn : cal_heads()
20396                         }
20397                     ]
20398                 },
20399                 { 
20400                     tag: 'tbody',
20401                     cn : cal_rows()
20402                 }
20403                   
20404             ]
20405         };
20406          
20407          var cfg = {
20408             cls : 'fc fc-ltr',
20409             cn : [
20410                 header,
20411                 {
20412                     cls : 'fc-content',
20413                     style : "position: relative;",
20414                     cn : [
20415                         {
20416                             cls : 'fc-view fc-view-month fc-grid',
20417                             style : 'position: relative',
20418                             unselectable : 'on',
20419                             cn : [
20420                                 {
20421                                     cls : 'fc-event-container',
20422                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20423                                 },
20424                                 cal_table
20425                             ]
20426                         }
20427                     ]
20428     
20429                 }
20430            ] 
20431             
20432         };
20433         
20434          
20435         
20436         return cfg;
20437     },
20438     
20439     
20440     initEvents : function()
20441     {
20442         if(!this.store){
20443             throw "can not find store for calendar";
20444         }
20445         
20446         var mark = {
20447             tag: "div",
20448             cls:"x-dlg-mask",
20449             style: "text-align:center",
20450             cn: [
20451                 {
20452                     tag: "div",
20453                     style: "background-color:white;width:50%;margin:250 auto",
20454                     cn: [
20455                         {
20456                             tag: "img",
20457                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20458                         },
20459                         {
20460                             tag: "span",
20461                             html: "Loading"
20462                         }
20463                         
20464                     ]
20465                 }
20466             ]
20467         };
20468         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20469         
20470         var size = this.el.select('.fc-content', true).first().getSize();
20471         this.maskEl.setSize(size.width, size.height);
20472         this.maskEl.enableDisplayMode("block");
20473         if(!this.loadMask){
20474             this.maskEl.hide();
20475         }
20476         
20477         this.store = Roo.factory(this.store, Roo.data);
20478         this.store.on('load', this.onLoad, this);
20479         this.store.on('beforeload', this.onBeforeLoad, this);
20480         
20481         this.resize();
20482         
20483         this.cells = this.el.select('.fc-day',true);
20484         //Roo.log(this.cells);
20485         this.textNodes = this.el.query('.fc-day-number');
20486         this.cells.addClassOnOver('fc-state-hover');
20487         
20488         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20489         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20490         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20491         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20492         
20493         this.on('monthchange', this.onMonthChange, this);
20494         
20495         this.update(new Date().clearTime());
20496     },
20497     
20498     resize : function() {
20499         var sz  = this.el.getSize();
20500         
20501         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20502         this.el.select('.fc-day-content div',true).setHeight(34);
20503     },
20504     
20505     
20506     // private
20507     showPrevMonth : function(e){
20508         this.update(this.activeDate.add("mo", -1));
20509     },
20510     showToday : function(e){
20511         this.update(new Date().clearTime());
20512     },
20513     // private
20514     showNextMonth : function(e){
20515         this.update(this.activeDate.add("mo", 1));
20516     },
20517
20518     // private
20519     showPrevYear : function(){
20520         this.update(this.activeDate.add("y", -1));
20521     },
20522
20523     // private
20524     showNextYear : function(){
20525         this.update(this.activeDate.add("y", 1));
20526     },
20527
20528     
20529    // private
20530     update : function(date)
20531     {
20532         var vd = this.activeDate;
20533         this.activeDate = date;
20534 //        if(vd && this.el){
20535 //            var t = date.getTime();
20536 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20537 //                Roo.log('using add remove');
20538 //                
20539 //                this.fireEvent('monthchange', this, date);
20540 //                
20541 //                this.cells.removeClass("fc-state-highlight");
20542 //                this.cells.each(function(c){
20543 //                   if(c.dateValue == t){
20544 //                       c.addClass("fc-state-highlight");
20545 //                       setTimeout(function(){
20546 //                            try{c.dom.firstChild.focus();}catch(e){}
20547 //                       }, 50);
20548 //                       return false;
20549 //                   }
20550 //                   return true;
20551 //                });
20552 //                return;
20553 //            }
20554 //        }
20555         
20556         var days = date.getDaysInMonth();
20557         
20558         var firstOfMonth = date.getFirstDateOfMonth();
20559         var startingPos = firstOfMonth.getDay()-this.startDay;
20560         
20561         if(startingPos < this.startDay){
20562             startingPos += 7;
20563         }
20564         
20565         var pm = date.add(Date.MONTH, -1);
20566         var prevStart = pm.getDaysInMonth()-startingPos;
20567 //        
20568         this.cells = this.el.select('.fc-day',true);
20569         this.textNodes = this.el.query('.fc-day-number');
20570         this.cells.addClassOnOver('fc-state-hover');
20571         
20572         var cells = this.cells.elements;
20573         var textEls = this.textNodes;
20574         
20575         Roo.each(cells, function(cell){
20576             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20577         });
20578         
20579         days += startingPos;
20580
20581         // convert everything to numbers so it's fast
20582         var day = 86400000;
20583         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20584         //Roo.log(d);
20585         //Roo.log(pm);
20586         //Roo.log(prevStart);
20587         
20588         var today = new Date().clearTime().getTime();
20589         var sel = date.clearTime().getTime();
20590         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20591         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20592         var ddMatch = this.disabledDatesRE;
20593         var ddText = this.disabledDatesText;
20594         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20595         var ddaysText = this.disabledDaysText;
20596         var format = this.format;
20597         
20598         var setCellClass = function(cal, cell){
20599             cell.row = 0;
20600             cell.events = [];
20601             cell.more = [];
20602             //Roo.log('set Cell Class');
20603             cell.title = "";
20604             var t = d.getTime();
20605             
20606             //Roo.log(d);
20607             
20608             cell.dateValue = t;
20609             if(t == today){
20610                 cell.className += " fc-today";
20611                 cell.className += " fc-state-highlight";
20612                 cell.title = cal.todayText;
20613             }
20614             if(t == sel){
20615                 // disable highlight in other month..
20616                 //cell.className += " fc-state-highlight";
20617                 
20618             }
20619             // disabling
20620             if(t < min) {
20621                 cell.className = " fc-state-disabled";
20622                 cell.title = cal.minText;
20623                 return;
20624             }
20625             if(t > max) {
20626                 cell.className = " fc-state-disabled";
20627                 cell.title = cal.maxText;
20628                 return;
20629             }
20630             if(ddays){
20631                 if(ddays.indexOf(d.getDay()) != -1){
20632                     cell.title = ddaysText;
20633                     cell.className = " fc-state-disabled";
20634                 }
20635             }
20636             if(ddMatch && format){
20637                 var fvalue = d.dateFormat(format);
20638                 if(ddMatch.test(fvalue)){
20639                     cell.title = ddText.replace("%0", fvalue);
20640                     cell.className = " fc-state-disabled";
20641                 }
20642             }
20643             
20644             if (!cell.initialClassName) {
20645                 cell.initialClassName = cell.dom.className;
20646             }
20647             
20648             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20649         };
20650
20651         var i = 0;
20652         
20653         for(; i < startingPos; i++) {
20654             textEls[i].innerHTML = (++prevStart);
20655             d.setDate(d.getDate()+1);
20656             
20657             cells[i].className = "fc-past fc-other-month";
20658             setCellClass(this, cells[i]);
20659         }
20660         
20661         var intDay = 0;
20662         
20663         for(; i < days; i++){
20664             intDay = i - startingPos + 1;
20665             textEls[i].innerHTML = (intDay);
20666             d.setDate(d.getDate()+1);
20667             
20668             cells[i].className = ''; // "x-date-active";
20669             setCellClass(this, cells[i]);
20670         }
20671         var extraDays = 0;
20672         
20673         for(; i < 42; i++) {
20674             textEls[i].innerHTML = (++extraDays);
20675             d.setDate(d.getDate()+1);
20676             
20677             cells[i].className = "fc-future fc-other-month";
20678             setCellClass(this, cells[i]);
20679         }
20680         
20681         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20682         
20683         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20684         
20685         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20686         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20687         
20688         if(totalRows != 6){
20689             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20690             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20691         }
20692         
20693         this.fireEvent('monthchange', this, date);
20694         
20695         
20696         /*
20697         if(!this.internalRender){
20698             var main = this.el.dom.firstChild;
20699             var w = main.offsetWidth;
20700             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20701             Roo.fly(main).setWidth(w);
20702             this.internalRender = true;
20703             // opera does not respect the auto grow header center column
20704             // then, after it gets a width opera refuses to recalculate
20705             // without a second pass
20706             if(Roo.isOpera && !this.secondPass){
20707                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20708                 this.secondPass = true;
20709                 this.update.defer(10, this, [date]);
20710             }
20711         }
20712         */
20713         
20714     },
20715     
20716     findCell : function(dt) {
20717         dt = dt.clearTime().getTime();
20718         var ret = false;
20719         this.cells.each(function(c){
20720             //Roo.log("check " +c.dateValue + '?=' + dt);
20721             if(c.dateValue == dt){
20722                 ret = c;
20723                 return false;
20724             }
20725             return true;
20726         });
20727         
20728         return ret;
20729     },
20730     
20731     findCells : function(ev) {
20732         var s = ev.start.clone().clearTime().getTime();
20733        // Roo.log(s);
20734         var e= ev.end.clone().clearTime().getTime();
20735        // Roo.log(e);
20736         var ret = [];
20737         this.cells.each(function(c){
20738              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20739             
20740             if(c.dateValue > e){
20741                 return ;
20742             }
20743             if(c.dateValue < s){
20744                 return ;
20745             }
20746             ret.push(c);
20747         });
20748         
20749         return ret;    
20750     },
20751     
20752 //    findBestRow: function(cells)
20753 //    {
20754 //        var ret = 0;
20755 //        
20756 //        for (var i =0 ; i < cells.length;i++) {
20757 //            ret  = Math.max(cells[i].rows || 0,ret);
20758 //        }
20759 //        return ret;
20760 //        
20761 //    },
20762     
20763     
20764     addItem : function(ev)
20765     {
20766         // look for vertical location slot in
20767         var cells = this.findCells(ev);
20768         
20769 //        ev.row = this.findBestRow(cells);
20770         
20771         // work out the location.
20772         
20773         var crow = false;
20774         var rows = [];
20775         for(var i =0; i < cells.length; i++) {
20776             
20777             cells[i].row = cells[0].row;
20778             
20779             if(i == 0){
20780                 cells[i].row = cells[i].row + 1;
20781             }
20782             
20783             if (!crow) {
20784                 crow = {
20785                     start : cells[i],
20786                     end :  cells[i]
20787                 };
20788                 continue;
20789             }
20790             if (crow.start.getY() == cells[i].getY()) {
20791                 // on same row.
20792                 crow.end = cells[i];
20793                 continue;
20794             }
20795             // different row.
20796             rows.push(crow);
20797             crow = {
20798                 start: cells[i],
20799                 end : cells[i]
20800             };
20801             
20802         }
20803         
20804         rows.push(crow);
20805         ev.els = [];
20806         ev.rows = rows;
20807         ev.cells = cells;
20808         
20809         cells[0].events.push(ev);
20810         
20811         this.calevents.push(ev);
20812     },
20813     
20814     clearEvents: function() {
20815         
20816         if(!this.calevents){
20817             return;
20818         }
20819         
20820         Roo.each(this.cells.elements, function(c){
20821             c.row = 0;
20822             c.events = [];
20823             c.more = [];
20824         });
20825         
20826         Roo.each(this.calevents, function(e) {
20827             Roo.each(e.els, function(el) {
20828                 el.un('mouseenter' ,this.onEventEnter, this);
20829                 el.un('mouseleave' ,this.onEventLeave, this);
20830                 el.remove();
20831             },this);
20832         },this);
20833         
20834         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20835             e.remove();
20836         });
20837         
20838     },
20839     
20840     renderEvents: function()
20841     {   
20842         var _this = this;
20843         
20844         this.cells.each(function(c) {
20845             
20846             if(c.row < 5){
20847                 return;
20848             }
20849             
20850             var ev = c.events;
20851             
20852             var r = 4;
20853             if(c.row != c.events.length){
20854                 r = 4 - (4 - (c.row - c.events.length));
20855             }
20856             
20857             c.events = ev.slice(0, r);
20858             c.more = ev.slice(r);
20859             
20860             if(c.more.length && c.more.length == 1){
20861                 c.events.push(c.more.pop());
20862             }
20863             
20864             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20865             
20866         });
20867             
20868         this.cells.each(function(c) {
20869             
20870             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20871             
20872             
20873             for (var e = 0; e < c.events.length; e++){
20874                 var ev = c.events[e];
20875                 var rows = ev.rows;
20876                 
20877                 for(var i = 0; i < rows.length; i++) {
20878                 
20879                     // how many rows should it span..
20880
20881                     var  cfg = {
20882                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20883                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20884
20885                         unselectable : "on",
20886                         cn : [
20887                             {
20888                                 cls: 'fc-event-inner',
20889                                 cn : [
20890     //                                {
20891     //                                  tag:'span',
20892     //                                  cls: 'fc-event-time',
20893     //                                  html : cells.length > 1 ? '' : ev.time
20894     //                                },
20895                                     {
20896                                       tag:'span',
20897                                       cls: 'fc-event-title',
20898                                       html : String.format('{0}', ev.title)
20899                                     }
20900
20901
20902                                 ]
20903                             },
20904                             {
20905                                 cls: 'ui-resizable-handle ui-resizable-e',
20906                                 html : '&nbsp;&nbsp;&nbsp'
20907                             }
20908
20909                         ]
20910                     };
20911
20912                     if (i == 0) {
20913                         cfg.cls += ' fc-event-start';
20914                     }
20915                     if ((i+1) == rows.length) {
20916                         cfg.cls += ' fc-event-end';
20917                     }
20918
20919                     var ctr = _this.el.select('.fc-event-container',true).first();
20920                     var cg = ctr.createChild(cfg);
20921
20922                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20923                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20924
20925                     var r = (c.more.length) ? 1 : 0;
20926                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20927                     cg.setWidth(ebox.right - sbox.x -2);
20928
20929                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20930                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20931                     cg.on('click', _this.onEventClick, _this, ev);
20932
20933                     ev.els.push(cg);
20934                     
20935                 }
20936                 
20937             }
20938             
20939             
20940             if(c.more.length){
20941                 var  cfg = {
20942                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20943                     style : 'position: absolute',
20944                     unselectable : "on",
20945                     cn : [
20946                         {
20947                             cls: 'fc-event-inner',
20948                             cn : [
20949                                 {
20950                                   tag:'span',
20951                                   cls: 'fc-event-title',
20952                                   html : 'More'
20953                                 }
20954
20955
20956                             ]
20957                         },
20958                         {
20959                             cls: 'ui-resizable-handle ui-resizable-e',
20960                             html : '&nbsp;&nbsp;&nbsp'
20961                         }
20962
20963                     ]
20964                 };
20965
20966                 var ctr = _this.el.select('.fc-event-container',true).first();
20967                 var cg = ctr.createChild(cfg);
20968
20969                 var sbox = c.select('.fc-day-content',true).first().getBox();
20970                 var ebox = c.select('.fc-day-content',true).first().getBox();
20971                 //Roo.log(cg);
20972                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20973                 cg.setWidth(ebox.right - sbox.x -2);
20974
20975                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20976                 
20977             }
20978             
20979         });
20980         
20981         
20982         
20983     },
20984     
20985     onEventEnter: function (e, el,event,d) {
20986         this.fireEvent('evententer', this, el, event);
20987     },
20988     
20989     onEventLeave: function (e, el,event,d) {
20990         this.fireEvent('eventleave', this, el, event);
20991     },
20992     
20993     onEventClick: function (e, el,event,d) {
20994         this.fireEvent('eventclick', this, el, event);
20995     },
20996     
20997     onMonthChange: function () {
20998         this.store.load();
20999     },
21000     
21001     onMoreEventClick: function(e, el, more)
21002     {
21003         var _this = this;
21004         
21005         this.calpopover.placement = 'right';
21006         this.calpopover.setTitle('More');
21007         
21008         this.calpopover.setContent('');
21009         
21010         var ctr = this.calpopover.el.select('.popover-content', true).first();
21011         
21012         Roo.each(more, function(m){
21013             var cfg = {
21014                 cls : 'fc-event-hori fc-event-draggable',
21015                 html : m.title
21016             };
21017             var cg = ctr.createChild(cfg);
21018             
21019             cg.on('click', _this.onEventClick, _this, m);
21020         });
21021         
21022         this.calpopover.show(el);
21023         
21024         
21025     },
21026     
21027     onLoad: function () 
21028     {   
21029         this.calevents = [];
21030         var cal = this;
21031         
21032         if(this.store.getCount() > 0){
21033             this.store.data.each(function(d){
21034                cal.addItem({
21035                     id : d.data.id,
21036                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21037                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21038                     time : d.data.start_time,
21039                     title : d.data.title,
21040                     description : d.data.description,
21041                     venue : d.data.venue
21042                 });
21043             });
21044         }
21045         
21046         this.renderEvents();
21047         
21048         if(this.calevents.length && this.loadMask){
21049             this.maskEl.hide();
21050         }
21051     },
21052     
21053     onBeforeLoad: function()
21054     {
21055         this.clearEvents();
21056         if(this.loadMask){
21057             this.maskEl.show();
21058         }
21059     }
21060 });
21061
21062  
21063  /*
21064  * - LGPL
21065  *
21066  * element
21067  * 
21068  */
21069
21070 /**
21071  * @class Roo.bootstrap.Popover
21072  * @extends Roo.bootstrap.Component
21073  * @builder-top
21074  * Bootstrap Popover class
21075  * @cfg {String} html contents of the popover   (or false to use children..)
21076  * @cfg {String} title of popover (or false to hide)
21077  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21078  * @cfg {String} trigger click || hover (or false to trigger manually)
21079  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21080  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21081  *      - if false and it has a 'parent' then it will be automatically added to that element
21082  *      - if string - Roo.get  will be called 
21083  * @cfg {Number} delay - delay before showing
21084  
21085  * @constructor
21086  * Create a new Popover
21087  * @param {Object} config The config object
21088  */
21089
21090 Roo.bootstrap.Popover = function(config){
21091     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21092     
21093     this.addEvents({
21094         // raw events
21095          /**
21096          * @event show
21097          * After the popover show
21098          * 
21099          * @param {Roo.bootstrap.Popover} this
21100          */
21101         "show" : true,
21102         /**
21103          * @event hide
21104          * After the popover hide
21105          * 
21106          * @param {Roo.bootstrap.Popover} this
21107          */
21108         "hide" : true
21109     });
21110 };
21111
21112 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21113     
21114     title: false,
21115     html: false,
21116     
21117     placement : 'right',
21118     trigger : 'hover', // hover
21119     modal : false,
21120     delay : 0,
21121     
21122     over: false,
21123     
21124     can_build_overlaid : false,
21125     
21126     maskEl : false, // the mask element
21127     headerEl : false,
21128     contentEl : false,
21129     alignEl : false, // when show is called with an element - this get's stored.
21130     
21131     getChildContainer : function()
21132     {
21133         return this.contentEl;
21134         
21135     },
21136     getPopoverHeader : function()
21137     {
21138         this.title = true; // flag not to hide it..
21139         this.headerEl.addClass('p-0');
21140         return this.headerEl
21141     },
21142     
21143     
21144     getAutoCreate : function(){
21145          
21146         var cfg = {
21147            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21148            style: 'display:block',
21149            cn : [
21150                 {
21151                     cls : 'arrow'
21152                 },
21153                 {
21154                     cls : 'popover-inner ',
21155                     cn : [
21156                         {
21157                             tag: 'h3',
21158                             cls: 'popover-title popover-header',
21159                             html : this.title === false ? '' : this.title
21160                         },
21161                         {
21162                             cls : 'popover-content popover-body '  + (this.cls || ''),
21163                             html : this.html || ''
21164                         }
21165                     ]
21166                     
21167                 }
21168            ]
21169         };
21170         
21171         return cfg;
21172     },
21173     /**
21174      * @param {string} the title
21175      */
21176     setTitle: function(str)
21177     {
21178         this.title = str;
21179         if (this.el) {
21180             this.headerEl.dom.innerHTML = str;
21181         }
21182         
21183     },
21184     /**
21185      * @param {string} the body content
21186      */
21187     setContent: function(str)
21188     {
21189         this.html = str;
21190         if (this.contentEl) {
21191             this.contentEl.dom.innerHTML = str;
21192         }
21193         
21194     },
21195     // as it get's added to the bottom of the page.
21196     onRender : function(ct, position)
21197     {
21198         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21199         
21200         
21201         
21202         if(!this.el){
21203             var cfg = Roo.apply({},  this.getAutoCreate());
21204             cfg.id = Roo.id();
21205             
21206             if (this.cls) {
21207                 cfg.cls += ' ' + this.cls;
21208             }
21209             if (this.style) {
21210                 cfg.style = this.style;
21211             }
21212             //Roo.log("adding to ");
21213             this.el = Roo.get(document.body).createChild(cfg, position);
21214 //            Roo.log(this.el);
21215         }
21216         
21217         this.contentEl = this.el.select('.popover-content',true).first();
21218         this.headerEl =  this.el.select('.popover-title',true).first();
21219         
21220         var nitems = [];
21221         if(typeof(this.items) != 'undefined'){
21222             var items = this.items;
21223             delete this.items;
21224
21225             for(var i =0;i < items.length;i++) {
21226                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21227             }
21228         }
21229
21230         this.items = nitems;
21231         
21232         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21233         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21234         
21235         
21236         
21237         this.initEvents();
21238     },
21239     
21240     resizeMask : function()
21241     {
21242         this.maskEl.setSize(
21243             Roo.lib.Dom.getViewWidth(true),
21244             Roo.lib.Dom.getViewHeight(true)
21245         );
21246     },
21247     
21248     initEvents : function()
21249     {
21250         
21251         if (!this.modal) { 
21252             Roo.bootstrap.Popover.register(this);
21253         }
21254          
21255         this.arrowEl = this.el.select('.arrow',true).first();
21256         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21257         this.el.enableDisplayMode('block');
21258         this.el.hide();
21259  
21260         
21261         if (this.over === false && !this.parent()) {
21262             return; 
21263         }
21264         if (this.triggers === false) {
21265             return;
21266         }
21267          
21268         // support parent
21269         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21270         var triggers = this.trigger ? this.trigger.split(' ') : [];
21271         Roo.each(triggers, function(trigger) {
21272         
21273             if (trigger == 'click') {
21274                 on_el.on('click', this.toggle, this);
21275             } else if (trigger != 'manual') {
21276                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21277                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21278       
21279                 on_el.on(eventIn  ,this.enter, this);
21280                 on_el.on(eventOut, this.leave, this);
21281             }
21282         }, this);
21283     },
21284     
21285     
21286     // private
21287     timeout : null,
21288     hoverState : null,
21289     
21290     toggle : function () {
21291         this.hoverState == 'in' ? this.leave() : this.enter();
21292     },
21293     
21294     enter : function () {
21295         
21296         clearTimeout(this.timeout);
21297     
21298         this.hoverState = 'in';
21299     
21300         if (!this.delay || !this.delay.show) {
21301             this.show();
21302             return;
21303         }
21304         var _t = this;
21305         this.timeout = setTimeout(function () {
21306             if (_t.hoverState == 'in') {
21307                 _t.show();
21308             }
21309         }, this.delay.show)
21310     },
21311     
21312     leave : function() {
21313         clearTimeout(this.timeout);
21314     
21315         this.hoverState = 'out';
21316     
21317         if (!this.delay || !this.delay.hide) {
21318             this.hide();
21319             return;
21320         }
21321         var _t = this;
21322         this.timeout = setTimeout(function () {
21323             if (_t.hoverState == 'out') {
21324                 _t.hide();
21325             }
21326         }, this.delay.hide)
21327     },
21328     /**
21329      * Show the popover
21330      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21331      * @param {string} (left|right|top|bottom) position
21332      */
21333     show : function (on_el, placement)
21334     {
21335         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21336         on_el = on_el || false; // default to false
21337          
21338         if (!on_el) {
21339             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21340                 on_el = this.parent().el;
21341             } else if (this.over) {
21342                 on_el = Roo.get(this.over);
21343             }
21344             
21345         }
21346         
21347         this.alignEl = Roo.get( on_el );
21348
21349         if (!this.el) {
21350             this.render(document.body);
21351         }
21352         
21353         
21354          
21355         
21356         if (this.title === false) {
21357             this.headerEl.hide();
21358         }
21359         
21360        
21361         this.el.show();
21362         this.el.dom.style.display = 'block';
21363          
21364  
21365         if (this.alignEl) {
21366             this.updatePosition(this.placement, true);
21367              
21368         } else {
21369             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21370             var es = this.el.getSize();
21371             var x = Roo.lib.Dom.getViewWidth()/2;
21372             var y = Roo.lib.Dom.getViewHeight()/2;
21373             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21374             
21375         }
21376
21377         
21378         //var arrow = this.el.select('.arrow',true).first();
21379         //arrow.set(align[2], 
21380         
21381         this.el.addClass('in');
21382         
21383          
21384         
21385         this.hoverState = 'in';
21386         
21387         if (this.modal) {
21388             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21389             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21390             this.maskEl.dom.style.display = 'block';
21391             this.maskEl.addClass('show');
21392         }
21393         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21394  
21395         this.fireEvent('show', this);
21396         
21397     },
21398     /**
21399      * fire this manually after loading a grid in the table for example
21400      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21401      * @param {Boolean} try and move it if we cant get right position.
21402      */
21403     updatePosition : function(placement, try_move)
21404     {
21405         // allow for calling with no parameters
21406         placement = placement   ? placement :  this.placement;
21407         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21408         
21409         this.el.removeClass([
21410             'fade','top','bottom', 'left', 'right','in',
21411             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21412         ]);
21413         this.el.addClass(placement + ' bs-popover-' + placement);
21414         
21415         if (!this.alignEl ) {
21416             return false;
21417         }
21418         
21419         switch (placement) {
21420             case 'right':
21421                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21422                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21423                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21424                     //normal display... or moved up/down.
21425                     this.el.setXY(offset);
21426                     var xy = this.alignEl.getAnchorXY('tr', false);
21427                     xy[0]+=2;xy[1]+=5;
21428                     this.arrowEl.setXY(xy);
21429                     return true;
21430                 }
21431                 // continue through...
21432                 return this.updatePosition('left', false);
21433                 
21434             
21435             case 'left':
21436                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21437                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21438                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21439                     //normal display... or moved up/down.
21440                     this.el.setXY(offset);
21441                     var xy = this.alignEl.getAnchorXY('tl', false);
21442                     xy[0]-=10;xy[1]+=5; // << fix me
21443                     this.arrowEl.setXY(xy);
21444                     return true;
21445                 }
21446                 // call self...
21447                 return this.updatePosition('right', false);
21448             
21449             case 'top':
21450                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21451                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21452                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21453                     //normal display... or moved up/down.
21454                     this.el.setXY(offset);
21455                     var xy = this.alignEl.getAnchorXY('t', false);
21456                     xy[1]-=10; // << fix me
21457                     this.arrowEl.setXY(xy);
21458                     return true;
21459                 }
21460                 // fall through
21461                return this.updatePosition('bottom', false);
21462             
21463             case 'bottom':
21464                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21465                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21466                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21467                     //normal display... or moved up/down.
21468                     this.el.setXY(offset);
21469                     var xy = this.alignEl.getAnchorXY('b', false);
21470                      xy[1]+=2; // << fix me
21471                     this.arrowEl.setXY(xy);
21472                     return true;
21473                 }
21474                 // fall through
21475                 return this.updatePosition('top', false);
21476                 
21477             
21478         }
21479         
21480         
21481         return false;
21482     },
21483     
21484     hide : function()
21485     {
21486         this.el.setXY([0,0]);
21487         this.el.removeClass('in');
21488         this.el.hide();
21489         this.hoverState = null;
21490         this.maskEl.hide(); // always..
21491         this.fireEvent('hide', this);
21492     }
21493     
21494 });
21495
21496
21497 Roo.apply(Roo.bootstrap.Popover, {
21498
21499     alignment : {
21500         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21501         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21502         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21503         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21504     },
21505     
21506     zIndex : 20001,
21507
21508     clickHander : false,
21509     
21510     
21511
21512     onMouseDown : function(e)
21513     {
21514         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21515             /// what is nothing is showing..
21516             this.hideAll();
21517         }
21518          
21519     },
21520     
21521     
21522     popups : [],
21523     
21524     register : function(popup)
21525     {
21526         if (!Roo.bootstrap.Popover.clickHandler) {
21527             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21528         }
21529         // hide other popups.
21530         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21531         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21532         this.hideAll(); //<< why?
21533         //this.popups.push(popup);
21534     },
21535     hideAll : function()
21536     {
21537         this.popups.forEach(function(p) {
21538             p.hide();
21539         });
21540     },
21541     onShow : function() {
21542         Roo.bootstrap.Popover.popups.push(this);
21543     },
21544     onHide : function() {
21545         Roo.bootstrap.Popover.popups.remove(this);
21546     } 
21547
21548 });/*
21549  * - LGPL
21550  *
21551  * Card header - holder for the card header elements.
21552  * 
21553  */
21554
21555 /**
21556  * @class Roo.bootstrap.PopoverNav
21557  * @extends Roo.bootstrap.NavGroup
21558  * Bootstrap Popover header navigation class
21559  * @constructor
21560  * Create a new Popover Header Navigation 
21561  * @param {Object} config The config object
21562  */
21563
21564 Roo.bootstrap.PopoverNav = function(config){
21565     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21566 };
21567
21568 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21569     
21570     
21571     container_method : 'getPopoverHeader' 
21572     
21573      
21574     
21575     
21576    
21577 });
21578
21579  
21580
21581  /*
21582  * - LGPL
21583  *
21584  * Progress
21585  * 
21586  */
21587
21588 /**
21589  * @class Roo.bootstrap.Progress
21590  * @extends Roo.bootstrap.Component
21591  * Bootstrap Progress class
21592  * @cfg {Boolean} striped striped of the progress bar
21593  * @cfg {Boolean} active animated of the progress bar
21594  * 
21595  * 
21596  * @constructor
21597  * Create a new Progress
21598  * @param {Object} config The config object
21599  */
21600
21601 Roo.bootstrap.Progress = function(config){
21602     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21603 };
21604
21605 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21606     
21607     striped : false,
21608     active: false,
21609     
21610     getAutoCreate : function(){
21611         var cfg = {
21612             tag: 'div',
21613             cls: 'progress'
21614         };
21615         
21616         
21617         if(this.striped){
21618             cfg.cls += ' progress-striped';
21619         }
21620       
21621         if(this.active){
21622             cfg.cls += ' active';
21623         }
21624         
21625         
21626         return cfg;
21627     }
21628    
21629 });
21630
21631  
21632
21633  /*
21634  * - LGPL
21635  *
21636  * ProgressBar
21637  * 
21638  */
21639
21640 /**
21641  * @class Roo.bootstrap.ProgressBar
21642  * @extends Roo.bootstrap.Component
21643  * Bootstrap ProgressBar class
21644  * @cfg {Number} aria_valuenow aria-value now
21645  * @cfg {Number} aria_valuemin aria-value min
21646  * @cfg {Number} aria_valuemax aria-value max
21647  * @cfg {String} label label for the progress bar
21648  * @cfg {String} panel (success | info | warning | danger )
21649  * @cfg {String} role role of the progress bar
21650  * @cfg {String} sr_only text
21651  * 
21652  * 
21653  * @constructor
21654  * Create a new ProgressBar
21655  * @param {Object} config The config object
21656  */
21657
21658 Roo.bootstrap.ProgressBar = function(config){
21659     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21660 };
21661
21662 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21663     
21664     aria_valuenow : 0,
21665     aria_valuemin : 0,
21666     aria_valuemax : 100,
21667     label : false,
21668     panel : false,
21669     role : false,
21670     sr_only: false,
21671     
21672     getAutoCreate : function()
21673     {
21674         
21675         var cfg = {
21676             tag: 'div',
21677             cls: 'progress-bar',
21678             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21679         };
21680         
21681         if(this.sr_only){
21682             cfg.cn = {
21683                 tag: 'span',
21684                 cls: 'sr-only',
21685                 html: this.sr_only
21686             }
21687         }
21688         
21689         if(this.role){
21690             cfg.role = this.role;
21691         }
21692         
21693         if(this.aria_valuenow){
21694             cfg['aria-valuenow'] = this.aria_valuenow;
21695         }
21696         
21697         if(this.aria_valuemin){
21698             cfg['aria-valuemin'] = this.aria_valuemin;
21699         }
21700         
21701         if(this.aria_valuemax){
21702             cfg['aria-valuemax'] = this.aria_valuemax;
21703         }
21704         
21705         if(this.label && !this.sr_only){
21706             cfg.html = this.label;
21707         }
21708         
21709         if(this.panel){
21710             cfg.cls += ' progress-bar-' + this.panel;
21711         }
21712         
21713         return cfg;
21714     },
21715     
21716     update : function(aria_valuenow)
21717     {
21718         this.aria_valuenow = aria_valuenow;
21719         
21720         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21721     }
21722    
21723 });
21724
21725  
21726
21727  /*
21728  * - LGPL
21729  *
21730  * column
21731  * 
21732  */
21733
21734 /**
21735  * @class Roo.bootstrap.TabGroup
21736  * @extends Roo.bootstrap.Column
21737  * Bootstrap Column class
21738  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21739  * @cfg {Boolean} carousel true to make the group behave like a carousel
21740  * @cfg {Boolean} bullets show bullets for the panels
21741  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21742  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21743  * @cfg {Boolean} showarrow (true|false) show arrow default true
21744  * 
21745  * @constructor
21746  * Create a new TabGroup
21747  * @param {Object} config The config object
21748  */
21749
21750 Roo.bootstrap.TabGroup = function(config){
21751     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21752     if (!this.navId) {
21753         this.navId = Roo.id();
21754     }
21755     this.tabs = [];
21756     Roo.bootstrap.TabGroup.register(this);
21757     
21758 };
21759
21760 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21761     
21762     carousel : false,
21763     transition : false,
21764     bullets : 0,
21765     timer : 0,
21766     autoslide : false,
21767     slideFn : false,
21768     slideOnTouch : false,
21769     showarrow : true,
21770     
21771     getAutoCreate : function()
21772     {
21773         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21774         
21775         cfg.cls += ' tab-content';
21776         
21777         if (this.carousel) {
21778             cfg.cls += ' carousel slide';
21779             
21780             cfg.cn = [{
21781                cls : 'carousel-inner',
21782                cn : []
21783             }];
21784         
21785             if(this.bullets  && !Roo.isTouch){
21786                 
21787                 var bullets = {
21788                     cls : 'carousel-bullets',
21789                     cn : []
21790                 };
21791                
21792                 if(this.bullets_cls){
21793                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21794                 }
21795                 
21796                 bullets.cn.push({
21797                     cls : 'clear'
21798                 });
21799                 
21800                 cfg.cn[0].cn.push(bullets);
21801             }
21802             
21803             if(this.showarrow){
21804                 cfg.cn[0].cn.push({
21805                     tag : 'div',
21806                     class : 'carousel-arrow',
21807                     cn : [
21808                         {
21809                             tag : 'div',
21810                             class : 'carousel-prev',
21811                             cn : [
21812                                 {
21813                                     tag : 'i',
21814                                     class : 'fa fa-chevron-left'
21815                                 }
21816                             ]
21817                         },
21818                         {
21819                             tag : 'div',
21820                             class : 'carousel-next',
21821                             cn : [
21822                                 {
21823                                     tag : 'i',
21824                                     class : 'fa fa-chevron-right'
21825                                 }
21826                             ]
21827                         }
21828                     ]
21829                 });
21830             }
21831             
21832         }
21833         
21834         return cfg;
21835     },
21836     
21837     initEvents:  function()
21838     {
21839 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21840 //            this.el.on("touchstart", this.onTouchStart, this);
21841 //        }
21842         
21843         if(this.autoslide){
21844             var _this = this;
21845             
21846             this.slideFn = window.setInterval(function() {
21847                 _this.showPanelNext();
21848             }, this.timer);
21849         }
21850         
21851         if(this.showarrow){
21852             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21853             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21854         }
21855         
21856         
21857     },
21858     
21859 //    onTouchStart : function(e, el, o)
21860 //    {
21861 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21862 //            return;
21863 //        }
21864 //        
21865 //        this.showPanelNext();
21866 //    },
21867     
21868     
21869     getChildContainer : function()
21870     {
21871         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21872     },
21873     
21874     /**
21875     * register a Navigation item
21876     * @param {Roo.bootstrap.NavItem} the navitem to add
21877     */
21878     register : function(item)
21879     {
21880         this.tabs.push( item);
21881         item.navId = this.navId; // not really needed..
21882         this.addBullet();
21883     
21884     },
21885     
21886     getActivePanel : function()
21887     {
21888         var r = false;
21889         Roo.each(this.tabs, function(t) {
21890             if (t.active) {
21891                 r = t;
21892                 return false;
21893             }
21894             return null;
21895         });
21896         return r;
21897         
21898     },
21899     getPanelByName : function(n)
21900     {
21901         var r = false;
21902         Roo.each(this.tabs, function(t) {
21903             if (t.tabId == n) {
21904                 r = t;
21905                 return false;
21906             }
21907             return null;
21908         });
21909         return r;
21910     },
21911     indexOfPanel : function(p)
21912     {
21913         var r = false;
21914         Roo.each(this.tabs, function(t,i) {
21915             if (t.tabId == p.tabId) {
21916                 r = i;
21917                 return false;
21918             }
21919             return null;
21920         });
21921         return r;
21922     },
21923     /**
21924      * show a specific panel
21925      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21926      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21927      */
21928     showPanel : function (pan)
21929     {
21930         if(this.transition || typeof(pan) == 'undefined'){
21931             Roo.log("waiting for the transitionend");
21932             return false;
21933         }
21934         
21935         if (typeof(pan) == 'number') {
21936             pan = this.tabs[pan];
21937         }
21938         
21939         if (typeof(pan) == 'string') {
21940             pan = this.getPanelByName(pan);
21941         }
21942         
21943         var cur = this.getActivePanel();
21944         
21945         if(!pan || !cur){
21946             Roo.log('pan or acitve pan is undefined');
21947             return false;
21948         }
21949         
21950         if (pan.tabId == this.getActivePanel().tabId) {
21951             return true;
21952         }
21953         
21954         if (false === cur.fireEvent('beforedeactivate')) {
21955             return false;
21956         }
21957         
21958         if(this.bullets > 0 && !Roo.isTouch){
21959             this.setActiveBullet(this.indexOfPanel(pan));
21960         }
21961         
21962         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21963             
21964             //class="carousel-item carousel-item-next carousel-item-left"
21965             
21966             this.transition = true;
21967             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21968             var lr = dir == 'next' ? 'left' : 'right';
21969             pan.el.addClass(dir); // or prev
21970             pan.el.addClass('carousel-item-' + dir); // or prev
21971             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21972             cur.el.addClass(lr); // or right
21973             pan.el.addClass(lr);
21974             cur.el.addClass('carousel-item-' +lr); // or right
21975             pan.el.addClass('carousel-item-' +lr);
21976             
21977             
21978             var _this = this;
21979             cur.el.on('transitionend', function() {
21980                 Roo.log("trans end?");
21981                 
21982                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21983                 pan.setActive(true);
21984                 
21985                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21986                 cur.setActive(false);
21987                 
21988                 _this.transition = false;
21989                 
21990             }, this, { single:  true } );
21991             
21992             return true;
21993         }
21994         
21995         cur.setActive(false);
21996         pan.setActive(true);
21997         
21998         return true;
21999         
22000     },
22001     showPanelNext : function()
22002     {
22003         var i = this.indexOfPanel(this.getActivePanel());
22004         
22005         if (i >= this.tabs.length - 1 && !this.autoslide) {
22006             return;
22007         }
22008         
22009         if (i >= this.tabs.length - 1 && this.autoslide) {
22010             i = -1;
22011         }
22012         
22013         this.showPanel(this.tabs[i+1]);
22014     },
22015     
22016     showPanelPrev : function()
22017     {
22018         var i = this.indexOfPanel(this.getActivePanel());
22019         
22020         if (i  < 1 && !this.autoslide) {
22021             return;
22022         }
22023         
22024         if (i < 1 && this.autoslide) {
22025             i = this.tabs.length;
22026         }
22027         
22028         this.showPanel(this.tabs[i-1]);
22029     },
22030     
22031     
22032     addBullet: function()
22033     {
22034         if(!this.bullets || Roo.isTouch){
22035             return;
22036         }
22037         var ctr = this.el.select('.carousel-bullets',true).first();
22038         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22039         var bullet = ctr.createChild({
22040             cls : 'bullet bullet-' + i
22041         },ctr.dom.lastChild);
22042         
22043         
22044         var _this = this;
22045         
22046         bullet.on('click', (function(e, el, o, ii, t){
22047
22048             e.preventDefault();
22049
22050             this.showPanel(ii);
22051
22052             if(this.autoslide && this.slideFn){
22053                 clearInterval(this.slideFn);
22054                 this.slideFn = window.setInterval(function() {
22055                     _this.showPanelNext();
22056                 }, this.timer);
22057             }
22058
22059         }).createDelegate(this, [i, bullet], true));
22060                 
22061         
22062     },
22063      
22064     setActiveBullet : function(i)
22065     {
22066         if(Roo.isTouch){
22067             return;
22068         }
22069         
22070         Roo.each(this.el.select('.bullet', true).elements, function(el){
22071             el.removeClass('selected');
22072         });
22073
22074         var bullet = this.el.select('.bullet-' + i, true).first();
22075         
22076         if(!bullet){
22077             return;
22078         }
22079         
22080         bullet.addClass('selected');
22081     }
22082     
22083     
22084   
22085 });
22086
22087  
22088
22089  
22090  
22091 Roo.apply(Roo.bootstrap.TabGroup, {
22092     
22093     groups: {},
22094      /**
22095     * register a Navigation Group
22096     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22097     */
22098     register : function(navgrp)
22099     {
22100         this.groups[navgrp.navId] = navgrp;
22101         
22102     },
22103     /**
22104     * fetch a Navigation Group based on the navigation ID
22105     * if one does not exist , it will get created.
22106     * @param {string} the navgroup to add
22107     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22108     */
22109     get: function(navId) {
22110         if (typeof(this.groups[navId]) == 'undefined') {
22111             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22112         }
22113         return this.groups[navId] ;
22114     }
22115     
22116     
22117     
22118 });
22119
22120  /*
22121  * - LGPL
22122  *
22123  * TabPanel
22124  * 
22125  */
22126
22127 /**
22128  * @class Roo.bootstrap.TabPanel
22129  * @extends Roo.bootstrap.Component
22130  * Bootstrap TabPanel class
22131  * @cfg {Boolean} active panel active
22132  * @cfg {String} html panel content
22133  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22134  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22135  * @cfg {String} href click to link..
22136  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22137  * 
22138  * 
22139  * @constructor
22140  * Create a new TabPanel
22141  * @param {Object} config The config object
22142  */
22143
22144 Roo.bootstrap.TabPanel = function(config){
22145     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22146     this.addEvents({
22147         /**
22148              * @event changed
22149              * Fires when the active status changes
22150              * @param {Roo.bootstrap.TabPanel} this
22151              * @param {Boolean} state the new state
22152             
22153          */
22154         'changed': true,
22155         /**
22156              * @event beforedeactivate
22157              * Fires before a tab is de-activated - can be used to do validation on a form.
22158              * @param {Roo.bootstrap.TabPanel} this
22159              * @return {Boolean} false if there is an error
22160             
22161          */
22162         'beforedeactivate': true
22163      });
22164     
22165     this.tabId = this.tabId || Roo.id();
22166   
22167 };
22168
22169 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22170     
22171     active: false,
22172     html: false,
22173     tabId: false,
22174     navId : false,
22175     href : '',
22176     touchSlide : false,
22177     getAutoCreate : function(){
22178         
22179         
22180         var cfg = {
22181             tag: 'div',
22182             // item is needed for carousel - not sure if it has any effect otherwise
22183             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22184             html: this.html || ''
22185         };
22186         
22187         if(this.active){
22188             cfg.cls += ' active';
22189         }
22190         
22191         if(this.tabId){
22192             cfg.tabId = this.tabId;
22193         }
22194         
22195         
22196         
22197         return cfg;
22198     },
22199     
22200     initEvents:  function()
22201     {
22202         var p = this.parent();
22203         
22204         this.navId = this.navId || p.navId;
22205         
22206         if (typeof(this.navId) != 'undefined') {
22207             // not really needed.. but just in case.. parent should be a NavGroup.
22208             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22209             
22210             tg.register(this);
22211             
22212             var i = tg.tabs.length - 1;
22213             
22214             if(this.active && tg.bullets > 0 && i < tg.bullets){
22215                 tg.setActiveBullet(i);
22216             }
22217         }
22218         
22219         this.el.on('click', this.onClick, this);
22220         
22221         if(Roo.isTouch && this.touchSlide){
22222             this.el.on("touchstart", this.onTouchStart, this);
22223             this.el.on("touchmove", this.onTouchMove, this);
22224             this.el.on("touchend", this.onTouchEnd, this);
22225         }
22226         
22227     },
22228     
22229     onRender : function(ct, position)
22230     {
22231         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22232     },
22233     
22234     setActive : function(state)
22235     {
22236         Roo.log("panel - set active " + this.tabId + "=" + state);
22237         
22238         this.active = state;
22239         if (!state) {
22240             this.el.removeClass('active');
22241             
22242         } else  if (!this.el.hasClass('active')) {
22243             this.el.addClass('active');
22244         }
22245         
22246         this.fireEvent('changed', this, state);
22247     },
22248     
22249     onClick : function(e)
22250     {
22251         e.preventDefault();
22252         
22253         if(!this.href.length){
22254             return;
22255         }
22256         
22257         window.location.href = this.href;
22258     },
22259     
22260     startX : 0,
22261     startY : 0,
22262     endX : 0,
22263     endY : 0,
22264     swiping : false,
22265     
22266     onTouchStart : function(e)
22267     {
22268         this.swiping = false;
22269         
22270         this.startX = e.browserEvent.touches[0].clientX;
22271         this.startY = e.browserEvent.touches[0].clientY;
22272     },
22273     
22274     onTouchMove : function(e)
22275     {
22276         this.swiping = true;
22277         
22278         this.endX = e.browserEvent.touches[0].clientX;
22279         this.endY = e.browserEvent.touches[0].clientY;
22280     },
22281     
22282     onTouchEnd : function(e)
22283     {
22284         if(!this.swiping){
22285             this.onClick(e);
22286             return;
22287         }
22288         
22289         var tabGroup = this.parent();
22290         
22291         if(this.endX > this.startX){ // swiping right
22292             tabGroup.showPanelPrev();
22293             return;
22294         }
22295         
22296         if(this.startX > this.endX){ // swiping left
22297             tabGroup.showPanelNext();
22298             return;
22299         }
22300     }
22301     
22302     
22303 });
22304  
22305
22306  
22307
22308  /*
22309  * - LGPL
22310  *
22311  * DateField
22312  * 
22313  */
22314
22315 /**
22316  * @class Roo.bootstrap.DateField
22317  * @extends Roo.bootstrap.Input
22318  * Bootstrap DateField class
22319  * @cfg {Number} weekStart default 0
22320  * @cfg {String} viewMode default empty, (months|years)
22321  * @cfg {String} minViewMode default empty, (months|years)
22322  * @cfg {Number} startDate default -Infinity
22323  * @cfg {Number} endDate default Infinity
22324  * @cfg {Boolean} todayHighlight default false
22325  * @cfg {Boolean} todayBtn default false
22326  * @cfg {Boolean} calendarWeeks default false
22327  * @cfg {Object} daysOfWeekDisabled default empty
22328  * @cfg {Boolean} singleMode default false (true | false)
22329  * 
22330  * @cfg {Boolean} keyboardNavigation default true
22331  * @cfg {String} language default en
22332  * 
22333  * @constructor
22334  * Create a new DateField
22335  * @param {Object} config The config object
22336  */
22337
22338 Roo.bootstrap.DateField = function(config){
22339     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22340      this.addEvents({
22341             /**
22342              * @event show
22343              * Fires when this field show.
22344              * @param {Roo.bootstrap.DateField} this
22345              * @param {Mixed} date The date value
22346              */
22347             show : true,
22348             /**
22349              * @event show
22350              * Fires when this field hide.
22351              * @param {Roo.bootstrap.DateField} this
22352              * @param {Mixed} date The date value
22353              */
22354             hide : true,
22355             /**
22356              * @event select
22357              * Fires when select a date.
22358              * @param {Roo.bootstrap.DateField} this
22359              * @param {Mixed} date The date value
22360              */
22361             select : true,
22362             /**
22363              * @event beforeselect
22364              * Fires when before select a date.
22365              * @param {Roo.bootstrap.DateField} this
22366              * @param {Mixed} date The date value
22367              */
22368             beforeselect : true
22369         });
22370 };
22371
22372 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22373     
22374     /**
22375      * @cfg {String} format
22376      * The default date format string which can be overriden for localization support.  The format must be
22377      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22378      */
22379     format : "m/d/y",
22380     /**
22381      * @cfg {String} altFormats
22382      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22383      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22384      */
22385     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22386     
22387     weekStart : 0,
22388     
22389     viewMode : '',
22390     
22391     minViewMode : '',
22392     
22393     todayHighlight : false,
22394     
22395     todayBtn: false,
22396     
22397     language: 'en',
22398     
22399     keyboardNavigation: true,
22400     
22401     calendarWeeks: false,
22402     
22403     startDate: -Infinity,
22404     
22405     endDate: Infinity,
22406     
22407     daysOfWeekDisabled: [],
22408     
22409     _events: [],
22410     
22411     singleMode : false,
22412     
22413     UTCDate: function()
22414     {
22415         return new Date(Date.UTC.apply(Date, arguments));
22416     },
22417     
22418     UTCToday: function()
22419     {
22420         var today = new Date();
22421         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22422     },
22423     
22424     getDate: function() {
22425             var d = this.getUTCDate();
22426             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22427     },
22428     
22429     getUTCDate: function() {
22430             return this.date;
22431     },
22432     
22433     setDate: function(d) {
22434             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22435     },
22436     
22437     setUTCDate: function(d) {
22438             this.date = d;
22439             this.setValue(this.formatDate(this.date));
22440     },
22441         
22442     onRender: function(ct, position)
22443     {
22444         
22445         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22446         
22447         this.language = this.language || 'en';
22448         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22449         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22450         
22451         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22452         this.format = this.format || 'm/d/y';
22453         this.isInline = false;
22454         this.isInput = true;
22455         this.component = this.el.select('.add-on', true).first() || false;
22456         this.component = (this.component && this.component.length === 0) ? false : this.component;
22457         this.hasInput = this.component && this.inputEl().length;
22458         
22459         if (typeof(this.minViewMode === 'string')) {
22460             switch (this.minViewMode) {
22461                 case 'months':
22462                     this.minViewMode = 1;
22463                     break;
22464                 case 'years':
22465                     this.minViewMode = 2;
22466                     break;
22467                 default:
22468                     this.minViewMode = 0;
22469                     break;
22470             }
22471         }
22472         
22473         if (typeof(this.viewMode === 'string')) {
22474             switch (this.viewMode) {
22475                 case 'months':
22476                     this.viewMode = 1;
22477                     break;
22478                 case 'years':
22479                     this.viewMode = 2;
22480                     break;
22481                 default:
22482                     this.viewMode = 0;
22483                     break;
22484             }
22485         }
22486                 
22487         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22488         
22489 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22490         
22491         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22492         
22493         this.picker().on('mousedown', this.onMousedown, this);
22494         this.picker().on('click', this.onClick, this);
22495         
22496         this.picker().addClass('datepicker-dropdown');
22497         
22498         this.startViewMode = this.viewMode;
22499         
22500         if(this.singleMode){
22501             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22502                 v.setVisibilityMode(Roo.Element.DISPLAY);
22503                 v.hide();
22504             });
22505             
22506             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22507                 v.setStyle('width', '189px');
22508             });
22509         }
22510         
22511         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22512             if(!this.calendarWeeks){
22513                 v.remove();
22514                 return;
22515             }
22516             
22517             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22518             v.attr('colspan', function(i, val){
22519                 return parseInt(val) + 1;
22520             });
22521         });
22522                         
22523         
22524         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22525         
22526         this.setStartDate(this.startDate);
22527         this.setEndDate(this.endDate);
22528         
22529         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22530         
22531         this.fillDow();
22532         this.fillMonths();
22533         this.update();
22534         this.showMode();
22535         
22536         if(this.isInline) {
22537             this.showPopup();
22538         }
22539     },
22540     
22541     picker : function()
22542     {
22543         return this.pickerEl;
22544 //        return this.el.select('.datepicker', true).first();
22545     },
22546     
22547     fillDow: function()
22548     {
22549         var dowCnt = this.weekStart;
22550         
22551         var dow = {
22552             tag: 'tr',
22553             cn: [
22554                 
22555             ]
22556         };
22557         
22558         if(this.calendarWeeks){
22559             dow.cn.push({
22560                 tag: 'th',
22561                 cls: 'cw',
22562                 html: '&nbsp;'
22563             })
22564         }
22565         
22566         while (dowCnt < this.weekStart + 7) {
22567             dow.cn.push({
22568                 tag: 'th',
22569                 cls: 'dow',
22570                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22571             });
22572         }
22573         
22574         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22575     },
22576     
22577     fillMonths: function()
22578     {    
22579         var i = 0;
22580         var months = this.picker().select('>.datepicker-months td', true).first();
22581         
22582         months.dom.innerHTML = '';
22583         
22584         while (i < 12) {
22585             var month = {
22586                 tag: 'span',
22587                 cls: 'month',
22588                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22589             };
22590             
22591             months.createChild(month);
22592         }
22593         
22594     },
22595     
22596     update: function()
22597     {
22598         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;
22599         
22600         if (this.date < this.startDate) {
22601             this.viewDate = new Date(this.startDate);
22602         } else if (this.date > this.endDate) {
22603             this.viewDate = new Date(this.endDate);
22604         } else {
22605             this.viewDate = new Date(this.date);
22606         }
22607         
22608         this.fill();
22609     },
22610     
22611     fill: function() 
22612     {
22613         var d = new Date(this.viewDate),
22614                 year = d.getUTCFullYear(),
22615                 month = d.getUTCMonth(),
22616                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22617                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22618                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22619                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22620                 currentDate = this.date && this.date.valueOf(),
22621                 today = this.UTCToday();
22622         
22623         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22624         
22625 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22626         
22627 //        this.picker.select('>tfoot th.today').
22628 //                                              .text(dates[this.language].today)
22629 //                                              .toggle(this.todayBtn !== false);
22630     
22631         this.updateNavArrows();
22632         this.fillMonths();
22633                                                 
22634         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22635         
22636         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22637          
22638         prevMonth.setUTCDate(day);
22639         
22640         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22641         
22642         var nextMonth = new Date(prevMonth);
22643         
22644         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22645         
22646         nextMonth = nextMonth.valueOf();
22647         
22648         var fillMonths = false;
22649         
22650         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22651         
22652         while(prevMonth.valueOf() <= nextMonth) {
22653             var clsName = '';
22654             
22655             if (prevMonth.getUTCDay() === this.weekStart) {
22656                 if(fillMonths){
22657                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22658                 }
22659                     
22660                 fillMonths = {
22661                     tag: 'tr',
22662                     cn: []
22663                 };
22664                 
22665                 if(this.calendarWeeks){
22666                     // ISO 8601: First week contains first thursday.
22667                     // ISO also states week starts on Monday, but we can be more abstract here.
22668                     var
22669                     // Start of current week: based on weekstart/current date
22670                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22671                     // Thursday of this week
22672                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22673                     // First Thursday of year, year from thursday
22674                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22675                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22676                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22677                     
22678                     fillMonths.cn.push({
22679                         tag: 'td',
22680                         cls: 'cw',
22681                         html: calWeek
22682                     });
22683                 }
22684             }
22685             
22686             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22687                 clsName += ' old';
22688             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22689                 clsName += ' new';
22690             }
22691             if (this.todayHighlight &&
22692                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22693                 prevMonth.getUTCMonth() == today.getMonth() &&
22694                 prevMonth.getUTCDate() == today.getDate()) {
22695                 clsName += ' today';
22696             }
22697             
22698             if (currentDate && prevMonth.valueOf() === currentDate) {
22699                 clsName += ' active';
22700             }
22701             
22702             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22703                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22704                     clsName += ' disabled';
22705             }
22706             
22707             fillMonths.cn.push({
22708                 tag: 'td',
22709                 cls: 'day ' + clsName,
22710                 html: prevMonth.getDate()
22711             });
22712             
22713             prevMonth.setDate(prevMonth.getDate()+1);
22714         }
22715           
22716         var currentYear = this.date && this.date.getUTCFullYear();
22717         var currentMonth = this.date && this.date.getUTCMonth();
22718         
22719         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22720         
22721         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22722             v.removeClass('active');
22723             
22724             if(currentYear === year && k === currentMonth){
22725                 v.addClass('active');
22726             }
22727             
22728             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22729                 v.addClass('disabled');
22730             }
22731             
22732         });
22733         
22734         
22735         year = parseInt(year/10, 10) * 10;
22736         
22737         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22738         
22739         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22740         
22741         year -= 1;
22742         for (var i = -1; i < 11; i++) {
22743             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22744                 tag: 'span',
22745                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22746                 html: year
22747             });
22748             
22749             year += 1;
22750         }
22751     },
22752     
22753     showMode: function(dir) 
22754     {
22755         if (dir) {
22756             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22757         }
22758         
22759         Roo.each(this.picker().select('>div',true).elements, function(v){
22760             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22761             v.hide();
22762         });
22763         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22764     },
22765     
22766     place: function()
22767     {
22768         if(this.isInline) {
22769             return;
22770         }
22771         
22772         this.picker().removeClass(['bottom', 'top']);
22773         
22774         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22775             /*
22776              * place to the top of element!
22777              *
22778              */
22779             
22780             this.picker().addClass('top');
22781             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22782             
22783             return;
22784         }
22785         
22786         this.picker().addClass('bottom');
22787         
22788         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22789     },
22790     
22791     parseDate : function(value)
22792     {
22793         if(!value || value instanceof Date){
22794             return value;
22795         }
22796         var v = Date.parseDate(value, this.format);
22797         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22798             v = Date.parseDate(value, 'Y-m-d');
22799         }
22800         if(!v && this.altFormats){
22801             if(!this.altFormatsArray){
22802                 this.altFormatsArray = this.altFormats.split("|");
22803             }
22804             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22805                 v = Date.parseDate(value, this.altFormatsArray[i]);
22806             }
22807         }
22808         return v;
22809     },
22810     
22811     formatDate : function(date, fmt)
22812     {   
22813         return (!date || !(date instanceof Date)) ?
22814         date : date.dateFormat(fmt || this.format);
22815     },
22816     
22817     onFocus : function()
22818     {
22819         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22820         this.showPopup();
22821     },
22822     
22823     onBlur : function()
22824     {
22825         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22826         
22827         var d = this.inputEl().getValue();
22828         
22829         this.setValue(d);
22830                 
22831         this.hidePopup();
22832     },
22833     
22834     showPopup : function()
22835     {
22836         this.picker().show();
22837         this.update();
22838         this.place();
22839         
22840         this.fireEvent('showpopup', this, this.date);
22841     },
22842     
22843     hidePopup : function()
22844     {
22845         if(this.isInline) {
22846             return;
22847         }
22848         this.picker().hide();
22849         this.viewMode = this.startViewMode;
22850         this.showMode();
22851         
22852         this.fireEvent('hidepopup', this, this.date);
22853         
22854     },
22855     
22856     onMousedown: function(e)
22857     {
22858         e.stopPropagation();
22859         e.preventDefault();
22860     },
22861     
22862     keyup: function(e)
22863     {
22864         Roo.bootstrap.DateField.superclass.keyup.call(this);
22865         this.update();
22866     },
22867
22868     setValue: function(v)
22869     {
22870         if(this.fireEvent('beforeselect', this, v) !== false){
22871             var d = new Date(this.parseDate(v) ).clearTime();
22872         
22873             if(isNaN(d.getTime())){
22874                 this.date = this.viewDate = '';
22875                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22876                 return;
22877             }
22878
22879             v = this.formatDate(d);
22880
22881             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22882
22883             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22884
22885             this.update();
22886
22887             this.fireEvent('select', this, this.date);
22888         }
22889     },
22890     
22891     getValue: function()
22892     {
22893         return this.formatDate(this.date);
22894     },
22895     
22896     fireKey: function(e)
22897     {
22898         if (!this.picker().isVisible()){
22899             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22900                 this.showPopup();
22901             }
22902             return;
22903         }
22904         
22905         var dateChanged = false,
22906         dir, day, month,
22907         newDate, newViewDate;
22908         
22909         switch(e.keyCode){
22910             case 27: // escape
22911                 this.hidePopup();
22912                 e.preventDefault();
22913                 break;
22914             case 37: // left
22915             case 39: // right
22916                 if (!this.keyboardNavigation) {
22917                     break;
22918                 }
22919                 dir = e.keyCode == 37 ? -1 : 1;
22920                 
22921                 if (e.ctrlKey){
22922                     newDate = this.moveYear(this.date, dir);
22923                     newViewDate = this.moveYear(this.viewDate, dir);
22924                 } else if (e.shiftKey){
22925                     newDate = this.moveMonth(this.date, dir);
22926                     newViewDate = this.moveMonth(this.viewDate, dir);
22927                 } else {
22928                     newDate = new Date(this.date);
22929                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22930                     newViewDate = new Date(this.viewDate);
22931                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22932                 }
22933                 if (this.dateWithinRange(newDate)){
22934                     this.date = newDate;
22935                     this.viewDate = newViewDate;
22936                     this.setValue(this.formatDate(this.date));
22937 //                    this.update();
22938                     e.preventDefault();
22939                     dateChanged = true;
22940                 }
22941                 break;
22942             case 38: // up
22943             case 40: // down
22944                 if (!this.keyboardNavigation) {
22945                     break;
22946                 }
22947                 dir = e.keyCode == 38 ? -1 : 1;
22948                 if (e.ctrlKey){
22949                     newDate = this.moveYear(this.date, dir);
22950                     newViewDate = this.moveYear(this.viewDate, dir);
22951                 } else if (e.shiftKey){
22952                     newDate = this.moveMonth(this.date, dir);
22953                     newViewDate = this.moveMonth(this.viewDate, dir);
22954                 } else {
22955                     newDate = new Date(this.date);
22956                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22957                     newViewDate = new Date(this.viewDate);
22958                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22959                 }
22960                 if (this.dateWithinRange(newDate)){
22961                     this.date = newDate;
22962                     this.viewDate = newViewDate;
22963                     this.setValue(this.formatDate(this.date));
22964 //                    this.update();
22965                     e.preventDefault();
22966                     dateChanged = true;
22967                 }
22968                 break;
22969             case 13: // enter
22970                 this.setValue(this.formatDate(this.date));
22971                 this.hidePopup();
22972                 e.preventDefault();
22973                 break;
22974             case 9: // tab
22975                 this.setValue(this.formatDate(this.date));
22976                 this.hidePopup();
22977                 break;
22978             case 16: // shift
22979             case 17: // ctrl
22980             case 18: // alt
22981                 break;
22982             default :
22983                 this.hidePopup();
22984                 
22985         }
22986     },
22987     
22988     
22989     onClick: function(e) 
22990     {
22991         e.stopPropagation();
22992         e.preventDefault();
22993         
22994         var target = e.getTarget();
22995         
22996         if(target.nodeName.toLowerCase() === 'i'){
22997             target = Roo.get(target).dom.parentNode;
22998         }
22999         
23000         var nodeName = target.nodeName;
23001         var className = target.className;
23002         var html = target.innerHTML;
23003         //Roo.log(nodeName);
23004         
23005         switch(nodeName.toLowerCase()) {
23006             case 'th':
23007                 switch(className) {
23008                     case 'switch':
23009                         this.showMode(1);
23010                         break;
23011                     case 'prev':
23012                     case 'next':
23013                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23014                         switch(this.viewMode){
23015                                 case 0:
23016                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23017                                         break;
23018                                 case 1:
23019                                 case 2:
23020                                         this.viewDate = this.moveYear(this.viewDate, dir);
23021                                         break;
23022                         }
23023                         this.fill();
23024                         break;
23025                     case 'today':
23026                         var date = new Date();
23027                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23028 //                        this.fill()
23029                         this.setValue(this.formatDate(this.date));
23030                         
23031                         this.hidePopup();
23032                         break;
23033                 }
23034                 break;
23035             case 'span':
23036                 if (className.indexOf('disabled') < 0) {
23037                 if (!this.viewDate) {
23038                     this.viewDate = new Date();
23039                 }
23040                 this.viewDate.setUTCDate(1);
23041                     if (className.indexOf('month') > -1) {
23042                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23043                     } else {
23044                         var year = parseInt(html, 10) || 0;
23045                         this.viewDate.setUTCFullYear(year);
23046                         
23047                     }
23048                     
23049                     if(this.singleMode){
23050                         this.setValue(this.formatDate(this.viewDate));
23051                         this.hidePopup();
23052                         return;
23053                     }
23054                     
23055                     this.showMode(-1);
23056                     this.fill();
23057                 }
23058                 break;
23059                 
23060             case 'td':
23061                 //Roo.log(className);
23062                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23063                     var day = parseInt(html, 10) || 1;
23064                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23065                         month = (this.viewDate || new Date()).getUTCMonth();
23066
23067                     if (className.indexOf('old') > -1) {
23068                         if(month === 0 ){
23069                             month = 11;
23070                             year -= 1;
23071                         }else{
23072                             month -= 1;
23073                         }
23074                     } else if (className.indexOf('new') > -1) {
23075                         if (month == 11) {
23076                             month = 0;
23077                             year += 1;
23078                         } else {
23079                             month += 1;
23080                         }
23081                     }
23082                     //Roo.log([year,month,day]);
23083                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23084                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23085 //                    this.fill();
23086                     //Roo.log(this.formatDate(this.date));
23087                     this.setValue(this.formatDate(this.date));
23088                     this.hidePopup();
23089                 }
23090                 break;
23091         }
23092     },
23093     
23094     setStartDate: function(startDate)
23095     {
23096         this.startDate = startDate || -Infinity;
23097         if (this.startDate !== -Infinity) {
23098             this.startDate = this.parseDate(this.startDate);
23099         }
23100         this.update();
23101         this.updateNavArrows();
23102     },
23103
23104     setEndDate: function(endDate)
23105     {
23106         this.endDate = endDate || Infinity;
23107         if (this.endDate !== Infinity) {
23108             this.endDate = this.parseDate(this.endDate);
23109         }
23110         this.update();
23111         this.updateNavArrows();
23112     },
23113     
23114     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23115     {
23116         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23117         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23118             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23119         }
23120         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23121             return parseInt(d, 10);
23122         });
23123         this.update();
23124         this.updateNavArrows();
23125     },
23126     
23127     updateNavArrows: function() 
23128     {
23129         if(this.singleMode){
23130             return;
23131         }
23132         
23133         var d = new Date(this.viewDate),
23134         year = d.getUTCFullYear(),
23135         month = d.getUTCMonth();
23136         
23137         Roo.each(this.picker().select('.prev', true).elements, function(v){
23138             v.show();
23139             switch (this.viewMode) {
23140                 case 0:
23141
23142                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23143                         v.hide();
23144                     }
23145                     break;
23146                 case 1:
23147                 case 2:
23148                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23149                         v.hide();
23150                     }
23151                     break;
23152             }
23153         });
23154         
23155         Roo.each(this.picker().select('.next', true).elements, function(v){
23156             v.show();
23157             switch (this.viewMode) {
23158                 case 0:
23159
23160                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23161                         v.hide();
23162                     }
23163                     break;
23164                 case 1:
23165                 case 2:
23166                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23167                         v.hide();
23168                     }
23169                     break;
23170             }
23171         })
23172     },
23173     
23174     moveMonth: function(date, dir)
23175     {
23176         if (!dir) {
23177             return date;
23178         }
23179         var new_date = new Date(date.valueOf()),
23180         day = new_date.getUTCDate(),
23181         month = new_date.getUTCMonth(),
23182         mag = Math.abs(dir),
23183         new_month, test;
23184         dir = dir > 0 ? 1 : -1;
23185         if (mag == 1){
23186             test = dir == -1
23187             // If going back one month, make sure month is not current month
23188             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23189             ? function(){
23190                 return new_date.getUTCMonth() == month;
23191             }
23192             // If going forward one month, make sure month is as expected
23193             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23194             : function(){
23195                 return new_date.getUTCMonth() != new_month;
23196             };
23197             new_month = month + dir;
23198             new_date.setUTCMonth(new_month);
23199             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23200             if (new_month < 0 || new_month > 11) {
23201                 new_month = (new_month + 12) % 12;
23202             }
23203         } else {
23204             // For magnitudes >1, move one month at a time...
23205             for (var i=0; i<mag; i++) {
23206                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23207                 new_date = this.moveMonth(new_date, dir);
23208             }
23209             // ...then reset the day, keeping it in the new month
23210             new_month = new_date.getUTCMonth();
23211             new_date.setUTCDate(day);
23212             test = function(){
23213                 return new_month != new_date.getUTCMonth();
23214             };
23215         }
23216         // Common date-resetting loop -- if date is beyond end of month, make it
23217         // end of month
23218         while (test()){
23219             new_date.setUTCDate(--day);
23220             new_date.setUTCMonth(new_month);
23221         }
23222         return new_date;
23223     },
23224
23225     moveYear: function(date, dir)
23226     {
23227         return this.moveMonth(date, dir*12);
23228     },
23229
23230     dateWithinRange: function(date)
23231     {
23232         return date >= this.startDate && date <= this.endDate;
23233     },
23234
23235     
23236     remove: function() 
23237     {
23238         this.picker().remove();
23239     },
23240     
23241     validateValue : function(value)
23242     {
23243         if(this.getVisibilityEl().hasClass('hidden')){
23244             return true;
23245         }
23246         
23247         if(value.length < 1)  {
23248             if(this.allowBlank){
23249                 return true;
23250             }
23251             return false;
23252         }
23253         
23254         if(value.length < this.minLength){
23255             return false;
23256         }
23257         if(value.length > this.maxLength){
23258             return false;
23259         }
23260         if(this.vtype){
23261             var vt = Roo.form.VTypes;
23262             if(!vt[this.vtype](value, this)){
23263                 return false;
23264             }
23265         }
23266         if(typeof this.validator == "function"){
23267             var msg = this.validator(value);
23268             if(msg !== true){
23269                 return false;
23270             }
23271         }
23272         
23273         if(this.regex && !this.regex.test(value)){
23274             return false;
23275         }
23276         
23277         if(typeof(this.parseDate(value)) == 'undefined'){
23278             return false;
23279         }
23280         
23281         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23282             return false;
23283         }      
23284         
23285         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23286             return false;
23287         } 
23288         
23289         
23290         return true;
23291     },
23292     
23293     reset : function()
23294     {
23295         this.date = this.viewDate = '';
23296         
23297         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23298     }
23299    
23300 });
23301
23302 Roo.apply(Roo.bootstrap.DateField,  {
23303     
23304     head : {
23305         tag: 'thead',
23306         cn: [
23307         {
23308             tag: 'tr',
23309             cn: [
23310             {
23311                 tag: 'th',
23312                 cls: 'prev',
23313                 html: '<i class="fa fa-arrow-left"/>'
23314             },
23315             {
23316                 tag: 'th',
23317                 cls: 'switch',
23318                 colspan: '5'
23319             },
23320             {
23321                 tag: 'th',
23322                 cls: 'next',
23323                 html: '<i class="fa fa-arrow-right"/>'
23324             }
23325
23326             ]
23327         }
23328         ]
23329     },
23330     
23331     content : {
23332         tag: 'tbody',
23333         cn: [
23334         {
23335             tag: 'tr',
23336             cn: [
23337             {
23338                 tag: 'td',
23339                 colspan: '7'
23340             }
23341             ]
23342         }
23343         ]
23344     },
23345     
23346     footer : {
23347         tag: 'tfoot',
23348         cn: [
23349         {
23350             tag: 'tr',
23351             cn: [
23352             {
23353                 tag: 'th',
23354                 colspan: '7',
23355                 cls: 'today'
23356             }
23357                     
23358             ]
23359         }
23360         ]
23361     },
23362     
23363     dates:{
23364         en: {
23365             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23366             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23367             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23368             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23369             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23370             today: "Today"
23371         }
23372     },
23373     
23374     modes: [
23375     {
23376         clsName: 'days',
23377         navFnc: 'Month',
23378         navStep: 1
23379     },
23380     {
23381         clsName: 'months',
23382         navFnc: 'FullYear',
23383         navStep: 1
23384     },
23385     {
23386         clsName: 'years',
23387         navFnc: 'FullYear',
23388         navStep: 10
23389     }]
23390 });
23391
23392 Roo.apply(Roo.bootstrap.DateField,  {
23393   
23394     template : {
23395         tag: 'div',
23396         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23397         cn: [
23398         {
23399             tag: 'div',
23400             cls: 'datepicker-days',
23401             cn: [
23402             {
23403                 tag: 'table',
23404                 cls: 'table-condensed',
23405                 cn:[
23406                 Roo.bootstrap.DateField.head,
23407                 {
23408                     tag: 'tbody'
23409                 },
23410                 Roo.bootstrap.DateField.footer
23411                 ]
23412             }
23413             ]
23414         },
23415         {
23416             tag: 'div',
23417             cls: 'datepicker-months',
23418             cn: [
23419             {
23420                 tag: 'table',
23421                 cls: 'table-condensed',
23422                 cn:[
23423                 Roo.bootstrap.DateField.head,
23424                 Roo.bootstrap.DateField.content,
23425                 Roo.bootstrap.DateField.footer
23426                 ]
23427             }
23428             ]
23429         },
23430         {
23431             tag: 'div',
23432             cls: 'datepicker-years',
23433             cn: [
23434             {
23435                 tag: 'table',
23436                 cls: 'table-condensed',
23437                 cn:[
23438                 Roo.bootstrap.DateField.head,
23439                 Roo.bootstrap.DateField.content,
23440                 Roo.bootstrap.DateField.footer
23441                 ]
23442             }
23443             ]
23444         }
23445         ]
23446     }
23447 });
23448
23449  
23450
23451  /*
23452  * - LGPL
23453  *
23454  * TimeField
23455  * 
23456  */
23457
23458 /**
23459  * @class Roo.bootstrap.TimeField
23460  * @extends Roo.bootstrap.Input
23461  * Bootstrap DateField class
23462  * 
23463  * 
23464  * @constructor
23465  * Create a new TimeField
23466  * @param {Object} config The config object
23467  */
23468
23469 Roo.bootstrap.TimeField = function(config){
23470     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23471     this.addEvents({
23472             /**
23473              * @event show
23474              * Fires when this field show.
23475              * @param {Roo.bootstrap.DateField} thisthis
23476              * @param {Mixed} date The date value
23477              */
23478             show : true,
23479             /**
23480              * @event show
23481              * Fires when this field hide.
23482              * @param {Roo.bootstrap.DateField} this
23483              * @param {Mixed} date The date value
23484              */
23485             hide : true,
23486             /**
23487              * @event select
23488              * Fires when select a date.
23489              * @param {Roo.bootstrap.DateField} this
23490              * @param {Mixed} date The date value
23491              */
23492             select : true
23493         });
23494 };
23495
23496 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23497     
23498     /**
23499      * @cfg {String} format
23500      * The default time format string which can be overriden for localization support.  The format must be
23501      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23502      */
23503     format : "H:i",
23504
23505     getAutoCreate : function()
23506     {
23507         this.after = '<i class="fa far fa-clock"></i>';
23508         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23509         
23510          
23511     },
23512     onRender: function(ct, position)
23513     {
23514         
23515         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23516                 
23517         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23518         
23519         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23520         
23521         this.pop = this.picker().select('>.datepicker-time',true).first();
23522         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23523         
23524         this.picker().on('mousedown', this.onMousedown, this);
23525         this.picker().on('click', this.onClick, this);
23526         
23527         this.picker().addClass('datepicker-dropdown');
23528     
23529         this.fillTime();
23530         this.update();
23531             
23532         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23533         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23534         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23535         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23536         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23537         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23538
23539     },
23540     
23541     fireKey: function(e){
23542         if (!this.picker().isVisible()){
23543             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23544                 this.show();
23545             }
23546             return;
23547         }
23548
23549         e.preventDefault();
23550         
23551         switch(e.keyCode){
23552             case 27: // escape
23553                 this.hide();
23554                 break;
23555             case 37: // left
23556             case 39: // right
23557                 this.onTogglePeriod();
23558                 break;
23559             case 38: // up
23560                 this.onIncrementMinutes();
23561                 break;
23562             case 40: // down
23563                 this.onDecrementMinutes();
23564                 break;
23565             case 13: // enter
23566             case 9: // tab
23567                 this.setTime();
23568                 break;
23569         }
23570     },
23571     
23572     onClick: function(e) {
23573         e.stopPropagation();
23574         e.preventDefault();
23575     },
23576     
23577     picker : function()
23578     {
23579         return this.pickerEl;
23580     },
23581     
23582     fillTime: function()
23583     {    
23584         var time = this.pop.select('tbody', true).first();
23585         
23586         time.dom.innerHTML = '';
23587         
23588         time.createChild({
23589             tag: 'tr',
23590             cn: [
23591                 {
23592                     tag: 'td',
23593                     cn: [
23594                         {
23595                             tag: 'a',
23596                             href: '#',
23597                             cls: 'btn',
23598                             cn: [
23599                                 {
23600                                     tag: 'i',
23601                                     cls: 'hours-up fa fas fa-chevron-up'
23602                                 }
23603                             ]
23604                         } 
23605                     ]
23606                 },
23607                 {
23608                     tag: 'td',
23609                     cls: 'separator'
23610                 },
23611                 {
23612                     tag: 'td',
23613                     cn: [
23614                         {
23615                             tag: 'a',
23616                             href: '#',
23617                             cls: 'btn',
23618                             cn: [
23619                                 {
23620                                     tag: 'i',
23621                                     cls: 'minutes-up fa fas fa-chevron-up'
23622                                 }
23623                             ]
23624                         }
23625                     ]
23626                 },
23627                 {
23628                     tag: 'td',
23629                     cls: 'separator'
23630                 }
23631             ]
23632         });
23633         
23634         time.createChild({
23635             tag: 'tr',
23636             cn: [
23637                 {
23638                     tag: 'td',
23639                     cn: [
23640                         {
23641                             tag: 'span',
23642                             cls: 'timepicker-hour',
23643                             html: '00'
23644                         }  
23645                     ]
23646                 },
23647                 {
23648                     tag: 'td',
23649                     cls: 'separator',
23650                     html: ':'
23651                 },
23652                 {
23653                     tag: 'td',
23654                     cn: [
23655                         {
23656                             tag: 'span',
23657                             cls: 'timepicker-minute',
23658                             html: '00'
23659                         }  
23660                     ]
23661                 },
23662                 {
23663                     tag: 'td',
23664                     cls: 'separator'
23665                 },
23666                 {
23667                     tag: 'td',
23668                     cn: [
23669                         {
23670                             tag: 'button',
23671                             type: 'button',
23672                             cls: 'btn btn-primary period',
23673                             html: 'AM'
23674                             
23675                         }
23676                     ]
23677                 }
23678             ]
23679         });
23680         
23681         time.createChild({
23682             tag: 'tr',
23683             cn: [
23684                 {
23685                     tag: 'td',
23686                     cn: [
23687                         {
23688                             tag: 'a',
23689                             href: '#',
23690                             cls: 'btn',
23691                             cn: [
23692                                 {
23693                                     tag: 'span',
23694                                     cls: 'hours-down fa fas fa-chevron-down'
23695                                 }
23696                             ]
23697                         }
23698                     ]
23699                 },
23700                 {
23701                     tag: 'td',
23702                     cls: 'separator'
23703                 },
23704                 {
23705                     tag: 'td',
23706                     cn: [
23707                         {
23708                             tag: 'a',
23709                             href: '#',
23710                             cls: 'btn',
23711                             cn: [
23712                                 {
23713                                     tag: 'span',
23714                                     cls: 'minutes-down fa fas fa-chevron-down'
23715                                 }
23716                             ]
23717                         }
23718                     ]
23719                 },
23720                 {
23721                     tag: 'td',
23722                     cls: 'separator'
23723                 }
23724             ]
23725         });
23726         
23727     },
23728     
23729     update: function()
23730     {
23731         
23732         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23733         
23734         this.fill();
23735     },
23736     
23737     fill: function() 
23738     {
23739         var hours = this.time.getHours();
23740         var minutes = this.time.getMinutes();
23741         var period = 'AM';
23742         
23743         if(hours > 11){
23744             period = 'PM';
23745         }
23746         
23747         if(hours == 0){
23748             hours = 12;
23749         }
23750         
23751         
23752         if(hours > 12){
23753             hours = hours - 12;
23754         }
23755         
23756         if(hours < 10){
23757             hours = '0' + hours;
23758         }
23759         
23760         if(minutes < 10){
23761             minutes = '0' + minutes;
23762         }
23763         
23764         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23765         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23766         this.pop.select('button', true).first().dom.innerHTML = period;
23767         
23768     },
23769     
23770     place: function()
23771     {   
23772         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23773         
23774         var cls = ['bottom'];
23775         
23776         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23777             cls.pop();
23778             cls.push('top');
23779         }
23780         
23781         cls.push('right');
23782         
23783         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23784             cls.pop();
23785             cls.push('left');
23786         }
23787         //this.picker().setXY(20000,20000);
23788         this.picker().addClass(cls.join('-'));
23789         
23790         var _this = this;
23791         
23792         Roo.each(cls, function(c){
23793             if(c == 'bottom'){
23794                 (function() {
23795                  //  
23796                 }).defer(200);
23797                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23798                 //_this.picker().setTop(_this.inputEl().getHeight());
23799                 return;
23800             }
23801             if(c == 'top'){
23802                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23803                 
23804                 //_this.picker().setTop(0 - _this.picker().getHeight());
23805                 return;
23806             }
23807             /*
23808             if(c == 'left'){
23809                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23810                 return;
23811             }
23812             if(c == 'right'){
23813                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23814                 return;
23815             }
23816             */
23817         });
23818         
23819     },
23820   
23821     onFocus : function()
23822     {
23823         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23824         this.show();
23825     },
23826     
23827     onBlur : function()
23828     {
23829         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23830         this.hide();
23831     },
23832     
23833     show : function()
23834     {
23835         this.picker().show();
23836         this.pop.show();
23837         this.update();
23838         this.place();
23839         
23840         this.fireEvent('show', this, this.date);
23841     },
23842     
23843     hide : function()
23844     {
23845         this.picker().hide();
23846         this.pop.hide();
23847         
23848         this.fireEvent('hide', this, this.date);
23849     },
23850     
23851     setTime : function()
23852     {
23853         this.hide();
23854         this.setValue(this.time.format(this.format));
23855         
23856         this.fireEvent('select', this, this.date);
23857         
23858         
23859     },
23860     
23861     onMousedown: function(e){
23862         e.stopPropagation();
23863         e.preventDefault();
23864     },
23865     
23866     onIncrementHours: function()
23867     {
23868         Roo.log('onIncrementHours');
23869         this.time = this.time.add(Date.HOUR, 1);
23870         this.update();
23871         
23872     },
23873     
23874     onDecrementHours: function()
23875     {
23876         Roo.log('onDecrementHours');
23877         this.time = this.time.add(Date.HOUR, -1);
23878         this.update();
23879     },
23880     
23881     onIncrementMinutes: function()
23882     {
23883         Roo.log('onIncrementMinutes');
23884         this.time = this.time.add(Date.MINUTE, 1);
23885         this.update();
23886     },
23887     
23888     onDecrementMinutes: function()
23889     {
23890         Roo.log('onDecrementMinutes');
23891         this.time = this.time.add(Date.MINUTE, -1);
23892         this.update();
23893     },
23894     
23895     onTogglePeriod: function()
23896     {
23897         Roo.log('onTogglePeriod');
23898         this.time = this.time.add(Date.HOUR, 12);
23899         this.update();
23900     }
23901     
23902    
23903 });
23904  
23905
23906 Roo.apply(Roo.bootstrap.TimeField,  {
23907   
23908     template : {
23909         tag: 'div',
23910         cls: 'datepicker dropdown-menu',
23911         cn: [
23912             {
23913                 tag: 'div',
23914                 cls: 'datepicker-time',
23915                 cn: [
23916                 {
23917                     tag: 'table',
23918                     cls: 'table-condensed',
23919                     cn:[
23920                         {
23921                             tag: 'tbody',
23922                             cn: [
23923                                 {
23924                                     tag: 'tr',
23925                                     cn: [
23926                                     {
23927                                         tag: 'td',
23928                                         colspan: '7'
23929                                     }
23930                                     ]
23931                                 }
23932                             ]
23933                         },
23934                         {
23935                             tag: 'tfoot',
23936                             cn: [
23937                                 {
23938                                     tag: 'tr',
23939                                     cn: [
23940                                     {
23941                                         tag: 'th',
23942                                         colspan: '7',
23943                                         cls: '',
23944                                         cn: [
23945                                             {
23946                                                 tag: 'button',
23947                                                 cls: 'btn btn-info ok',
23948                                                 html: 'OK'
23949                                             }
23950                                         ]
23951                                     }
23952                     
23953                                     ]
23954                                 }
23955                             ]
23956                         }
23957                     ]
23958                 }
23959                 ]
23960             }
23961         ]
23962     }
23963 });
23964
23965  
23966
23967  /*
23968  * - LGPL
23969  *
23970  * MonthField
23971  * 
23972  */
23973
23974 /**
23975  * @class Roo.bootstrap.MonthField
23976  * @extends Roo.bootstrap.Input
23977  * Bootstrap MonthField class
23978  * 
23979  * @cfg {String} language default en
23980  * 
23981  * @constructor
23982  * Create a new MonthField
23983  * @param {Object} config The config object
23984  */
23985
23986 Roo.bootstrap.MonthField = function(config){
23987     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23988     
23989     this.addEvents({
23990         /**
23991          * @event show
23992          * Fires when this field show.
23993          * @param {Roo.bootstrap.MonthField} this
23994          * @param {Mixed} date The date value
23995          */
23996         show : true,
23997         /**
23998          * @event show
23999          * Fires when this field hide.
24000          * @param {Roo.bootstrap.MonthField} this
24001          * @param {Mixed} date The date value
24002          */
24003         hide : true,
24004         /**
24005          * @event select
24006          * Fires when select a date.
24007          * @param {Roo.bootstrap.MonthField} this
24008          * @param {String} oldvalue The old value
24009          * @param {String} newvalue The new value
24010          */
24011         select : true
24012     });
24013 };
24014
24015 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24016     
24017     onRender: function(ct, position)
24018     {
24019         
24020         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24021         
24022         this.language = this.language || 'en';
24023         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24024         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24025         
24026         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24027         this.isInline = false;
24028         this.isInput = true;
24029         this.component = this.el.select('.add-on', true).first() || false;
24030         this.component = (this.component && this.component.length === 0) ? false : this.component;
24031         this.hasInput = this.component && this.inputEL().length;
24032         
24033         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24034         
24035         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24036         
24037         this.picker().on('mousedown', this.onMousedown, this);
24038         this.picker().on('click', this.onClick, this);
24039         
24040         this.picker().addClass('datepicker-dropdown');
24041         
24042         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24043             v.setStyle('width', '189px');
24044         });
24045         
24046         this.fillMonths();
24047         
24048         this.update();
24049         
24050         if(this.isInline) {
24051             this.show();
24052         }
24053         
24054     },
24055     
24056     setValue: function(v, suppressEvent)
24057     {   
24058         var o = this.getValue();
24059         
24060         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24061         
24062         this.update();
24063
24064         if(suppressEvent !== true){
24065             this.fireEvent('select', this, o, v);
24066         }
24067         
24068     },
24069     
24070     getValue: function()
24071     {
24072         return this.value;
24073     },
24074     
24075     onClick: function(e) 
24076     {
24077         e.stopPropagation();
24078         e.preventDefault();
24079         
24080         var target = e.getTarget();
24081         
24082         if(target.nodeName.toLowerCase() === 'i'){
24083             target = Roo.get(target).dom.parentNode;
24084         }
24085         
24086         var nodeName = target.nodeName;
24087         var className = target.className;
24088         var html = target.innerHTML;
24089         
24090         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24091             return;
24092         }
24093         
24094         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24095         
24096         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24097         
24098         this.hide();
24099                         
24100     },
24101     
24102     picker : function()
24103     {
24104         return this.pickerEl;
24105     },
24106     
24107     fillMonths: function()
24108     {    
24109         var i = 0;
24110         var months = this.picker().select('>.datepicker-months td', true).first();
24111         
24112         months.dom.innerHTML = '';
24113         
24114         while (i < 12) {
24115             var month = {
24116                 tag: 'span',
24117                 cls: 'month',
24118                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24119             };
24120             
24121             months.createChild(month);
24122         }
24123         
24124     },
24125     
24126     update: function()
24127     {
24128         var _this = this;
24129         
24130         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24131             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24132         }
24133         
24134         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24135             e.removeClass('active');
24136             
24137             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24138                 e.addClass('active');
24139             }
24140         })
24141     },
24142     
24143     place: function()
24144     {
24145         if(this.isInline) {
24146             return;
24147         }
24148         
24149         this.picker().removeClass(['bottom', 'top']);
24150         
24151         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24152             /*
24153              * place to the top of element!
24154              *
24155              */
24156             
24157             this.picker().addClass('top');
24158             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24159             
24160             return;
24161         }
24162         
24163         this.picker().addClass('bottom');
24164         
24165         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24166     },
24167     
24168     onFocus : function()
24169     {
24170         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24171         this.show();
24172     },
24173     
24174     onBlur : function()
24175     {
24176         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24177         
24178         var d = this.inputEl().getValue();
24179         
24180         this.setValue(d);
24181                 
24182         this.hide();
24183     },
24184     
24185     show : function()
24186     {
24187         this.picker().show();
24188         this.picker().select('>.datepicker-months', true).first().show();
24189         this.update();
24190         this.place();
24191         
24192         this.fireEvent('show', this, this.date);
24193     },
24194     
24195     hide : function()
24196     {
24197         if(this.isInline) {
24198             return;
24199         }
24200         this.picker().hide();
24201         this.fireEvent('hide', this, this.date);
24202         
24203     },
24204     
24205     onMousedown: function(e)
24206     {
24207         e.stopPropagation();
24208         e.preventDefault();
24209     },
24210     
24211     keyup: function(e)
24212     {
24213         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24214         this.update();
24215     },
24216
24217     fireKey: function(e)
24218     {
24219         if (!this.picker().isVisible()){
24220             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24221                 this.show();
24222             }
24223             return;
24224         }
24225         
24226         var dir;
24227         
24228         switch(e.keyCode){
24229             case 27: // escape
24230                 this.hide();
24231                 e.preventDefault();
24232                 break;
24233             case 37: // left
24234             case 39: // right
24235                 dir = e.keyCode == 37 ? -1 : 1;
24236                 
24237                 this.vIndex = this.vIndex + dir;
24238                 
24239                 if(this.vIndex < 0){
24240                     this.vIndex = 0;
24241                 }
24242                 
24243                 if(this.vIndex > 11){
24244                     this.vIndex = 11;
24245                 }
24246                 
24247                 if(isNaN(this.vIndex)){
24248                     this.vIndex = 0;
24249                 }
24250                 
24251                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24252                 
24253                 break;
24254             case 38: // up
24255             case 40: // down
24256                 
24257                 dir = e.keyCode == 38 ? -1 : 1;
24258                 
24259                 this.vIndex = this.vIndex + dir * 4;
24260                 
24261                 if(this.vIndex < 0){
24262                     this.vIndex = 0;
24263                 }
24264                 
24265                 if(this.vIndex > 11){
24266                     this.vIndex = 11;
24267                 }
24268                 
24269                 if(isNaN(this.vIndex)){
24270                     this.vIndex = 0;
24271                 }
24272                 
24273                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24274                 break;
24275                 
24276             case 13: // enter
24277                 
24278                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24279                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24280                 }
24281                 
24282                 this.hide();
24283                 e.preventDefault();
24284                 break;
24285             case 9: // tab
24286                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24287                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24288                 }
24289                 this.hide();
24290                 break;
24291             case 16: // shift
24292             case 17: // ctrl
24293             case 18: // alt
24294                 break;
24295             default :
24296                 this.hide();
24297                 
24298         }
24299     },
24300     
24301     remove: function() 
24302     {
24303         this.picker().remove();
24304     }
24305    
24306 });
24307
24308 Roo.apply(Roo.bootstrap.MonthField,  {
24309     
24310     content : {
24311         tag: 'tbody',
24312         cn: [
24313         {
24314             tag: 'tr',
24315             cn: [
24316             {
24317                 tag: 'td',
24318                 colspan: '7'
24319             }
24320             ]
24321         }
24322         ]
24323     },
24324     
24325     dates:{
24326         en: {
24327             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24328             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24329         }
24330     }
24331 });
24332
24333 Roo.apply(Roo.bootstrap.MonthField,  {
24334   
24335     template : {
24336         tag: 'div',
24337         cls: 'datepicker dropdown-menu roo-dynamic',
24338         cn: [
24339             {
24340                 tag: 'div',
24341                 cls: 'datepicker-months',
24342                 cn: [
24343                 {
24344                     tag: 'table',
24345                     cls: 'table-condensed',
24346                     cn:[
24347                         Roo.bootstrap.DateField.content
24348                     ]
24349                 }
24350                 ]
24351             }
24352         ]
24353     }
24354 });
24355
24356  
24357
24358  
24359  /*
24360  * - LGPL
24361  *
24362  * CheckBox
24363  * 
24364  */
24365
24366 /**
24367  * @class Roo.bootstrap.CheckBox
24368  * @extends Roo.bootstrap.Input
24369  * Bootstrap CheckBox class
24370  * 
24371  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24372  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24373  * @cfg {String} boxLabel The text that appears beside the checkbox
24374  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24375  * @cfg {Boolean} checked initnal the element
24376  * @cfg {Boolean} inline inline the element (default false)
24377  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24378  * @cfg {String} tooltip label tooltip
24379  * 
24380  * @constructor
24381  * Create a new CheckBox
24382  * @param {Object} config The config object
24383  */
24384
24385 Roo.bootstrap.CheckBox = function(config){
24386     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24387    
24388     this.addEvents({
24389         /**
24390         * @event check
24391         * Fires when the element is checked or unchecked.
24392         * @param {Roo.bootstrap.CheckBox} this This input
24393         * @param {Boolean} checked The new checked value
24394         */
24395        check : true,
24396        /**
24397         * @event click
24398         * Fires when the element is click.
24399         * @param {Roo.bootstrap.CheckBox} this This input
24400         */
24401        click : true
24402     });
24403     
24404 };
24405
24406 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24407   
24408     inputType: 'checkbox',
24409     inputValue: 1,
24410     valueOff: 0,
24411     boxLabel: false,
24412     checked: false,
24413     weight : false,
24414     inline: false,
24415     tooltip : '',
24416     
24417     // checkbox success does not make any sense really.. 
24418     invalidClass : "",
24419     validClass : "",
24420     
24421     
24422     getAutoCreate : function()
24423     {
24424         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24425         
24426         var id = Roo.id();
24427         
24428         var cfg = {};
24429         
24430         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24431         
24432         if(this.inline){
24433             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24434         }
24435         
24436         var input =  {
24437             tag: 'input',
24438             id : id,
24439             type : this.inputType,
24440             value : this.inputValue,
24441             cls : 'roo-' + this.inputType, //'form-box',
24442             placeholder : this.placeholder || ''
24443             
24444         };
24445         
24446         if(this.inputType != 'radio'){
24447             var hidden =  {
24448                 tag: 'input',
24449                 type : 'hidden',
24450                 cls : 'roo-hidden-value',
24451                 value : this.checked ? this.inputValue : this.valueOff
24452             };
24453         }
24454         
24455             
24456         if (this.weight) { // Validity check?
24457             cfg.cls += " " + this.inputType + "-" + this.weight;
24458         }
24459         
24460         if (this.disabled) {
24461             input.disabled=true;
24462         }
24463         
24464         if(this.checked){
24465             input.checked = this.checked;
24466         }
24467         
24468         if (this.name) {
24469             
24470             input.name = this.name;
24471             
24472             if(this.inputType != 'radio'){
24473                 hidden.name = this.name;
24474                 input.name = '_hidden_' + this.name;
24475             }
24476         }
24477         
24478         if (this.size) {
24479             input.cls += ' input-' + this.size;
24480         }
24481         
24482         var settings=this;
24483         
24484         ['xs','sm','md','lg'].map(function(size){
24485             if (settings[size]) {
24486                 cfg.cls += ' col-' + size + '-' + settings[size];
24487             }
24488         });
24489         
24490         var inputblock = input;
24491          
24492         if (this.before || this.after) {
24493             
24494             inputblock = {
24495                 cls : 'input-group',
24496                 cn :  [] 
24497             };
24498             
24499             if (this.before) {
24500                 inputblock.cn.push({
24501                     tag :'span',
24502                     cls : 'input-group-addon',
24503                     html : this.before
24504                 });
24505             }
24506             
24507             inputblock.cn.push(input);
24508             
24509             if(this.inputType != 'radio'){
24510                 inputblock.cn.push(hidden);
24511             }
24512             
24513             if (this.after) {
24514                 inputblock.cn.push({
24515                     tag :'span',
24516                     cls : 'input-group-addon',
24517                     html : this.after
24518                 });
24519             }
24520             
24521         }
24522         var boxLabelCfg = false;
24523         
24524         if(this.boxLabel){
24525            
24526             boxLabelCfg = {
24527                 tag: 'label',
24528                 //'for': id, // box label is handled by onclick - so no for...
24529                 cls: 'box-label',
24530                 html: this.boxLabel
24531             };
24532             if(this.tooltip){
24533                 boxLabelCfg.tooltip = this.tooltip;
24534             }
24535              
24536         }
24537         
24538         
24539         if (align ==='left' && this.fieldLabel.length) {
24540 //                Roo.log("left and has label");
24541             cfg.cn = [
24542                 {
24543                     tag: 'label',
24544                     'for' :  id,
24545                     cls : 'control-label',
24546                     html : this.fieldLabel
24547                 },
24548                 {
24549                     cls : "", 
24550                     cn: [
24551                         inputblock
24552                     ]
24553                 }
24554             ];
24555             
24556             if (boxLabelCfg) {
24557                 cfg.cn[1].cn.push(boxLabelCfg);
24558             }
24559             
24560             if(this.labelWidth > 12){
24561                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24562             }
24563             
24564             if(this.labelWidth < 13 && this.labelmd == 0){
24565                 this.labelmd = this.labelWidth;
24566             }
24567             
24568             if(this.labellg > 0){
24569                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24570                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24571             }
24572             
24573             if(this.labelmd > 0){
24574                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24575                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24576             }
24577             
24578             if(this.labelsm > 0){
24579                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24580                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24581             }
24582             
24583             if(this.labelxs > 0){
24584                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24585                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24586             }
24587             
24588         } else if ( this.fieldLabel.length) {
24589 //                Roo.log(" label");
24590                 cfg.cn = [
24591                    
24592                     {
24593                         tag: this.boxLabel ? 'span' : 'label',
24594                         'for': id,
24595                         cls: 'control-label box-input-label',
24596                         //cls : 'input-group-addon',
24597                         html : this.fieldLabel
24598                     },
24599                     
24600                     inputblock
24601                     
24602                 ];
24603                 if (boxLabelCfg) {
24604                     cfg.cn.push(boxLabelCfg);
24605                 }
24606
24607         } else {
24608             
24609 //                Roo.log(" no label && no align");
24610                 cfg.cn = [  inputblock ] ;
24611                 if (boxLabelCfg) {
24612                     cfg.cn.push(boxLabelCfg);
24613                 }
24614
24615                 
24616         }
24617         
24618        
24619         
24620         if(this.inputType != 'radio'){
24621             cfg.cn.push(hidden);
24622         }
24623         
24624         return cfg;
24625         
24626     },
24627     
24628     /**
24629      * return the real input element.
24630      */
24631     inputEl: function ()
24632     {
24633         return this.el.select('input.roo-' + this.inputType,true).first();
24634     },
24635     hiddenEl: function ()
24636     {
24637         return this.el.select('input.roo-hidden-value',true).first();
24638     },
24639     
24640     labelEl: function()
24641     {
24642         return this.el.select('label.control-label',true).first();
24643     },
24644     /* depricated... */
24645     
24646     label: function()
24647     {
24648         return this.labelEl();
24649     },
24650     
24651     boxLabelEl: function()
24652     {
24653         return this.el.select('label.box-label',true).first();
24654     },
24655     
24656     initEvents : function()
24657     {
24658 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24659         
24660         this.inputEl().on('click', this.onClick,  this);
24661         
24662         if (this.boxLabel) { 
24663             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24664         }
24665         
24666         this.startValue = this.getValue();
24667         
24668         if(this.groupId){
24669             Roo.bootstrap.CheckBox.register(this);
24670         }
24671     },
24672     
24673     onClick : function(e)
24674     {   
24675         if(this.fireEvent('click', this, e) !== false){
24676             this.setChecked(!this.checked);
24677         }
24678         
24679     },
24680     
24681     setChecked : function(state,suppressEvent)
24682     {
24683         this.startValue = this.getValue();
24684
24685         if(this.inputType == 'radio'){
24686             
24687             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24688                 e.dom.checked = false;
24689             });
24690             
24691             this.inputEl().dom.checked = true;
24692             
24693             this.inputEl().dom.value = this.inputValue;
24694             
24695             if(suppressEvent !== true){
24696                 this.fireEvent('check', this, true);
24697             }
24698             
24699             this.validate();
24700             
24701             return;
24702         }
24703         
24704         this.checked = state;
24705         
24706         this.inputEl().dom.checked = state;
24707         
24708         
24709         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24710         
24711         if(suppressEvent !== true){
24712             this.fireEvent('check', this, state);
24713         }
24714         
24715         this.validate();
24716     },
24717     
24718     getValue : function()
24719     {
24720         if(this.inputType == 'radio'){
24721             return this.getGroupValue();
24722         }
24723         
24724         return this.hiddenEl().dom.value;
24725         
24726     },
24727     
24728     getGroupValue : function()
24729     {
24730         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24731             return '';
24732         }
24733         
24734         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24735     },
24736     
24737     setValue : function(v,suppressEvent)
24738     {
24739         if(this.inputType == 'radio'){
24740             this.setGroupValue(v, suppressEvent);
24741             return;
24742         }
24743         
24744         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24745         
24746         this.validate();
24747     },
24748     
24749     setGroupValue : function(v, suppressEvent)
24750     {
24751         this.startValue = this.getValue();
24752         
24753         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24754             e.dom.checked = false;
24755             
24756             if(e.dom.value == v){
24757                 e.dom.checked = true;
24758             }
24759         });
24760         
24761         if(suppressEvent !== true){
24762             this.fireEvent('check', this, true);
24763         }
24764
24765         this.validate();
24766         
24767         return;
24768     },
24769     
24770     validate : function()
24771     {
24772         if(this.getVisibilityEl().hasClass('hidden')){
24773             return true;
24774         }
24775         
24776         if(
24777                 this.disabled || 
24778                 (this.inputType == 'radio' && this.validateRadio()) ||
24779                 (this.inputType == 'checkbox' && this.validateCheckbox())
24780         ){
24781             this.markValid();
24782             return true;
24783         }
24784         
24785         this.markInvalid();
24786         return false;
24787     },
24788     
24789     validateRadio : function()
24790     {
24791         if(this.getVisibilityEl().hasClass('hidden')){
24792             return true;
24793         }
24794         
24795         if(this.allowBlank){
24796             return true;
24797         }
24798         
24799         var valid = false;
24800         
24801         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24802             if(!e.dom.checked){
24803                 return;
24804             }
24805             
24806             valid = true;
24807             
24808             return false;
24809         });
24810         
24811         return valid;
24812     },
24813     
24814     validateCheckbox : function()
24815     {
24816         if(!this.groupId){
24817             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24818             //return (this.getValue() == this.inputValue) ? true : false;
24819         }
24820         
24821         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24822         
24823         if(!group){
24824             return false;
24825         }
24826         
24827         var r = false;
24828         
24829         for(var i in group){
24830             if(group[i].el.isVisible(true)){
24831                 r = false;
24832                 break;
24833             }
24834             
24835             r = true;
24836         }
24837         
24838         for(var i in group){
24839             if(r){
24840                 break;
24841             }
24842             
24843             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24844         }
24845         
24846         return r;
24847     },
24848     
24849     /**
24850      * Mark this field as valid
24851      */
24852     markValid : function()
24853     {
24854         var _this = this;
24855         
24856         this.fireEvent('valid', this);
24857         
24858         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24859         
24860         if(this.groupId){
24861             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24862         }
24863         
24864         if(label){
24865             label.markValid();
24866         }
24867
24868         if(this.inputType == 'radio'){
24869             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24870                 var fg = e.findParent('.form-group', false, true);
24871                 if (Roo.bootstrap.version == 3) {
24872                     fg.removeClass([_this.invalidClass, _this.validClass]);
24873                     fg.addClass(_this.validClass);
24874                 } else {
24875                     fg.removeClass(['is-valid', 'is-invalid']);
24876                     fg.addClass('is-valid');
24877                 }
24878             });
24879             
24880             return;
24881         }
24882
24883         if(!this.groupId){
24884             var fg = this.el.findParent('.form-group', false, true);
24885             if (Roo.bootstrap.version == 3) {
24886                 fg.removeClass([this.invalidClass, this.validClass]);
24887                 fg.addClass(this.validClass);
24888             } else {
24889                 fg.removeClass(['is-valid', 'is-invalid']);
24890                 fg.addClass('is-valid');
24891             }
24892             return;
24893         }
24894         
24895         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24896         
24897         if(!group){
24898             return;
24899         }
24900         
24901         for(var i in group){
24902             var fg = group[i].el.findParent('.form-group', false, true);
24903             if (Roo.bootstrap.version == 3) {
24904                 fg.removeClass([this.invalidClass, this.validClass]);
24905                 fg.addClass(this.validClass);
24906             } else {
24907                 fg.removeClass(['is-valid', 'is-invalid']);
24908                 fg.addClass('is-valid');
24909             }
24910         }
24911     },
24912     
24913      /**
24914      * Mark this field as invalid
24915      * @param {String} msg The validation message
24916      */
24917     markInvalid : function(msg)
24918     {
24919         if(this.allowBlank){
24920             return;
24921         }
24922         
24923         var _this = this;
24924         
24925         this.fireEvent('invalid', this, msg);
24926         
24927         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24928         
24929         if(this.groupId){
24930             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24931         }
24932         
24933         if(label){
24934             label.markInvalid();
24935         }
24936             
24937         if(this.inputType == 'radio'){
24938             
24939             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24940                 var fg = e.findParent('.form-group', false, true);
24941                 if (Roo.bootstrap.version == 3) {
24942                     fg.removeClass([_this.invalidClass, _this.validClass]);
24943                     fg.addClass(_this.invalidClass);
24944                 } else {
24945                     fg.removeClass(['is-invalid', 'is-valid']);
24946                     fg.addClass('is-invalid');
24947                 }
24948             });
24949             
24950             return;
24951         }
24952         
24953         if(!this.groupId){
24954             var fg = this.el.findParent('.form-group', false, true);
24955             if (Roo.bootstrap.version == 3) {
24956                 fg.removeClass([_this.invalidClass, _this.validClass]);
24957                 fg.addClass(_this.invalidClass);
24958             } else {
24959                 fg.removeClass(['is-invalid', 'is-valid']);
24960                 fg.addClass('is-invalid');
24961             }
24962             return;
24963         }
24964         
24965         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24966         
24967         if(!group){
24968             return;
24969         }
24970         
24971         for(var i in group){
24972             var fg = group[i].el.findParent('.form-group', false, true);
24973             if (Roo.bootstrap.version == 3) {
24974                 fg.removeClass([_this.invalidClass, _this.validClass]);
24975                 fg.addClass(_this.invalidClass);
24976             } else {
24977                 fg.removeClass(['is-invalid', 'is-valid']);
24978                 fg.addClass('is-invalid');
24979             }
24980         }
24981         
24982     },
24983     
24984     clearInvalid : function()
24985     {
24986         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24987         
24988         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24989         
24990         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24991         
24992         if (label && label.iconEl) {
24993             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24994             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24995         }
24996     },
24997     
24998     disable : function()
24999     {
25000         if(this.inputType != 'radio'){
25001             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25002             return;
25003         }
25004         
25005         var _this = this;
25006         
25007         if(this.rendered){
25008             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25009                 _this.getActionEl().addClass(this.disabledClass);
25010                 e.dom.disabled = true;
25011             });
25012         }
25013         
25014         this.disabled = true;
25015         this.fireEvent("disable", this);
25016         return this;
25017     },
25018
25019     enable : function()
25020     {
25021         if(this.inputType != 'radio'){
25022             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25023             return;
25024         }
25025         
25026         var _this = this;
25027         
25028         if(this.rendered){
25029             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25030                 _this.getActionEl().removeClass(this.disabledClass);
25031                 e.dom.disabled = false;
25032             });
25033         }
25034         
25035         this.disabled = false;
25036         this.fireEvent("enable", this);
25037         return this;
25038     },
25039     
25040     setBoxLabel : function(v)
25041     {
25042         this.boxLabel = v;
25043         
25044         if(this.rendered){
25045             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25046         }
25047     }
25048
25049 });
25050
25051 Roo.apply(Roo.bootstrap.CheckBox, {
25052     
25053     groups: {},
25054     
25055      /**
25056     * register a CheckBox Group
25057     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25058     */
25059     register : function(checkbox)
25060     {
25061         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25062             this.groups[checkbox.groupId] = {};
25063         }
25064         
25065         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25066             return;
25067         }
25068         
25069         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25070         
25071     },
25072     /**
25073     * fetch a CheckBox Group based on the group ID
25074     * @param {string} the group ID
25075     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25076     */
25077     get: function(groupId) {
25078         if (typeof(this.groups[groupId]) == 'undefined') {
25079             return false;
25080         }
25081         
25082         return this.groups[groupId] ;
25083     }
25084     
25085     
25086 });
25087 /*
25088  * - LGPL
25089  *
25090  * RadioItem
25091  * 
25092  */
25093
25094 /**
25095  * @class Roo.bootstrap.Radio
25096  * @extends Roo.bootstrap.Component
25097  * Bootstrap Radio class
25098  * @cfg {String} boxLabel - the label associated
25099  * @cfg {String} value - the value of radio
25100  * 
25101  * @constructor
25102  * Create a new Radio
25103  * @param {Object} config The config object
25104  */
25105 Roo.bootstrap.Radio = function(config){
25106     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25107     
25108 };
25109
25110 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25111     
25112     boxLabel : '',
25113     
25114     value : '',
25115     
25116     getAutoCreate : function()
25117     {
25118         var cfg = {
25119             tag : 'div',
25120             cls : 'form-group radio',
25121             cn : [
25122                 {
25123                     tag : 'label',
25124                     cls : 'box-label',
25125                     html : this.boxLabel
25126                 }
25127             ]
25128         };
25129         
25130         return cfg;
25131     },
25132     
25133     initEvents : function() 
25134     {
25135         this.parent().register(this);
25136         
25137         this.el.on('click', this.onClick, this);
25138         
25139     },
25140     
25141     onClick : function(e)
25142     {
25143         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25144             this.setChecked(true);
25145         }
25146     },
25147     
25148     setChecked : function(state, suppressEvent)
25149     {
25150         this.parent().setValue(this.value, suppressEvent);
25151         
25152     },
25153     
25154     setBoxLabel : function(v)
25155     {
25156         this.boxLabel = v;
25157         
25158         if(this.rendered){
25159             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25160         }
25161     }
25162     
25163 });
25164  
25165
25166  /*
25167  * - LGPL
25168  *
25169  * Input
25170  * 
25171  */
25172
25173 /**
25174  * @class Roo.bootstrap.SecurePass
25175  * @extends Roo.bootstrap.Input
25176  * Bootstrap SecurePass class
25177  *
25178  * 
25179  * @constructor
25180  * Create a new SecurePass
25181  * @param {Object} config The config object
25182  */
25183  
25184 Roo.bootstrap.SecurePass = function (config) {
25185     // these go here, so the translation tool can replace them..
25186     this.errors = {
25187         PwdEmpty: "Please type a password, and then retype it to confirm.",
25188         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25189         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25190         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25191         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25192         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25193         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25194         TooWeak: "Your password is Too Weak."
25195     },
25196     this.meterLabel = "Password strength:";
25197     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25198     this.meterClass = [
25199         "roo-password-meter-tooweak", 
25200         "roo-password-meter-weak", 
25201         "roo-password-meter-medium", 
25202         "roo-password-meter-strong", 
25203         "roo-password-meter-grey"
25204     ];
25205     
25206     this.errors = {};
25207     
25208     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25209 }
25210
25211 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25212     /**
25213      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25214      * {
25215      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25216      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25217      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25218      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25219      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25220      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25221      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25222      * })
25223      */
25224     // private
25225     
25226     meterWidth: 300,
25227     errorMsg :'',    
25228     errors: false,
25229     imageRoot: '/',
25230     /**
25231      * @cfg {String/Object} Label for the strength meter (defaults to
25232      * 'Password strength:')
25233      */
25234     // private
25235     meterLabel: '',
25236     /**
25237      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25238      * ['Weak', 'Medium', 'Strong'])
25239      */
25240     // private    
25241     pwdStrengths: false,    
25242     // private
25243     strength: 0,
25244     // private
25245     _lastPwd: null,
25246     // private
25247     kCapitalLetter: 0,
25248     kSmallLetter: 1,
25249     kDigit: 2,
25250     kPunctuation: 3,
25251     
25252     insecure: false,
25253     // private
25254     initEvents: function ()
25255     {
25256         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25257
25258         if (this.el.is('input[type=password]') && Roo.isSafari) {
25259             this.el.on('keydown', this.SafariOnKeyDown, this);
25260         }
25261
25262         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25263     },
25264     // private
25265     onRender: function (ct, position)
25266     {
25267         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25268         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25269         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25270
25271         this.trigger.createChild({
25272                    cn: [
25273                     {
25274                     //id: 'PwdMeter',
25275                     tag: 'div',
25276                     cls: 'roo-password-meter-grey col-xs-12',
25277                     style: {
25278                         //width: 0,
25279                         //width: this.meterWidth + 'px'                                                
25280                         }
25281                     },
25282                     {                            
25283                          cls: 'roo-password-meter-text'                          
25284                     }
25285                 ]            
25286         });
25287
25288          
25289         if (this.hideTrigger) {
25290             this.trigger.setDisplayed(false);
25291         }
25292         this.setSize(this.width || '', this.height || '');
25293     },
25294     // private
25295     onDestroy: function ()
25296     {
25297         if (this.trigger) {
25298             this.trigger.removeAllListeners();
25299             this.trigger.remove();
25300         }
25301         if (this.wrap) {
25302             this.wrap.remove();
25303         }
25304         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25305     },
25306     // private
25307     checkStrength: function ()
25308     {
25309         var pwd = this.inputEl().getValue();
25310         if (pwd == this._lastPwd) {
25311             return;
25312         }
25313
25314         var strength;
25315         if (this.ClientSideStrongPassword(pwd)) {
25316             strength = 3;
25317         } else if (this.ClientSideMediumPassword(pwd)) {
25318             strength = 2;
25319         } else if (this.ClientSideWeakPassword(pwd)) {
25320             strength = 1;
25321         } else {
25322             strength = 0;
25323         }
25324         
25325         Roo.log('strength1: ' + strength);
25326         
25327         //var pm = this.trigger.child('div/div/div').dom;
25328         var pm = this.trigger.child('div/div');
25329         pm.removeClass(this.meterClass);
25330         pm.addClass(this.meterClass[strength]);
25331                 
25332         
25333         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25334                 
25335         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25336         
25337         this._lastPwd = pwd;
25338     },
25339     reset: function ()
25340     {
25341         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25342         
25343         this._lastPwd = '';
25344         
25345         var pm = this.trigger.child('div/div');
25346         pm.removeClass(this.meterClass);
25347         pm.addClass('roo-password-meter-grey');        
25348         
25349         
25350         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25351         
25352         pt.innerHTML = '';
25353         this.inputEl().dom.type='password';
25354     },
25355     // private
25356     validateValue: function (value)
25357     {
25358         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25359             return false;
25360         }
25361         if (value.length == 0) {
25362             if (this.allowBlank) {
25363                 this.clearInvalid();
25364                 return true;
25365             }
25366
25367             this.markInvalid(this.errors.PwdEmpty);
25368             this.errorMsg = this.errors.PwdEmpty;
25369             return false;
25370         }
25371         
25372         if(this.insecure){
25373             return true;
25374         }
25375         
25376         if (!value.match(/[\x21-\x7e]+/)) {
25377             this.markInvalid(this.errors.PwdBadChar);
25378             this.errorMsg = this.errors.PwdBadChar;
25379             return false;
25380         }
25381         if (value.length < 6) {
25382             this.markInvalid(this.errors.PwdShort);
25383             this.errorMsg = this.errors.PwdShort;
25384             return false;
25385         }
25386         if (value.length > 16) {
25387             this.markInvalid(this.errors.PwdLong);
25388             this.errorMsg = this.errors.PwdLong;
25389             return false;
25390         }
25391         var strength;
25392         if (this.ClientSideStrongPassword(value)) {
25393             strength = 3;
25394         } else if (this.ClientSideMediumPassword(value)) {
25395             strength = 2;
25396         } else if (this.ClientSideWeakPassword(value)) {
25397             strength = 1;
25398         } else {
25399             strength = 0;
25400         }
25401
25402         
25403         if (strength < 2) {
25404             //this.markInvalid(this.errors.TooWeak);
25405             this.errorMsg = this.errors.TooWeak;
25406             //return false;
25407         }
25408         
25409         
25410         console.log('strength2: ' + strength);
25411         
25412         //var pm = this.trigger.child('div/div/div').dom;
25413         
25414         var pm = this.trigger.child('div/div');
25415         pm.removeClass(this.meterClass);
25416         pm.addClass(this.meterClass[strength]);
25417                 
25418         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25419                 
25420         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25421         
25422         this.errorMsg = ''; 
25423         return true;
25424     },
25425     // private
25426     CharacterSetChecks: function (type)
25427     {
25428         this.type = type;
25429         this.fResult = false;
25430     },
25431     // private
25432     isctype: function (character, type)
25433     {
25434         switch (type) {  
25435             case this.kCapitalLetter:
25436                 if (character >= 'A' && character <= 'Z') {
25437                     return true;
25438                 }
25439                 break;
25440             
25441             case this.kSmallLetter:
25442                 if (character >= 'a' && character <= 'z') {
25443                     return true;
25444                 }
25445                 break;
25446             
25447             case this.kDigit:
25448                 if (character >= '0' && character <= '9') {
25449                     return true;
25450                 }
25451                 break;
25452             
25453             case this.kPunctuation:
25454                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25455                     return true;
25456                 }
25457                 break;
25458             
25459             default:
25460                 return false;
25461         }
25462
25463     },
25464     // private
25465     IsLongEnough: function (pwd, size)
25466     {
25467         return !(pwd == null || isNaN(size) || pwd.length < size);
25468     },
25469     // private
25470     SpansEnoughCharacterSets: function (word, nb)
25471     {
25472         if (!this.IsLongEnough(word, nb))
25473         {
25474             return false;
25475         }
25476
25477         var characterSetChecks = new Array(
25478             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25479             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25480         );
25481         
25482         for (var index = 0; index < word.length; ++index) {
25483             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25484                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25485                     characterSetChecks[nCharSet].fResult = true;
25486                     break;
25487                 }
25488             }
25489         }
25490
25491         var nCharSets = 0;
25492         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25493             if (characterSetChecks[nCharSet].fResult) {
25494                 ++nCharSets;
25495             }
25496         }
25497
25498         if (nCharSets < nb) {
25499             return false;
25500         }
25501         return true;
25502     },
25503     // private
25504     ClientSideStrongPassword: function (pwd)
25505     {
25506         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25507     },
25508     // private
25509     ClientSideMediumPassword: function (pwd)
25510     {
25511         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25512     },
25513     // private
25514     ClientSideWeakPassword: function (pwd)
25515     {
25516         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25517     }
25518           
25519 })//<script type="text/javascript">
25520
25521 /*
25522  * Based  Ext JS Library 1.1.1
25523  * Copyright(c) 2006-2007, Ext JS, LLC.
25524  * LGPL
25525  *
25526  */
25527  
25528 /**
25529  * @class Roo.HtmlEditorCore
25530  * @extends Roo.Component
25531  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25532  *
25533  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25534  */
25535
25536 Roo.HtmlEditorCore = function(config){
25537     
25538     
25539     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25540     
25541     
25542     this.addEvents({
25543         /**
25544          * @event initialize
25545          * Fires when the editor is fully initialized (including the iframe)
25546          * @param {Roo.HtmlEditorCore} this
25547          */
25548         initialize: true,
25549         /**
25550          * @event activate
25551          * Fires when the editor is first receives the focus. Any insertion must wait
25552          * until after this event.
25553          * @param {Roo.HtmlEditorCore} this
25554          */
25555         activate: true,
25556          /**
25557          * @event beforesync
25558          * Fires before the textarea is updated with content from the editor iframe. Return false
25559          * to cancel the sync.
25560          * @param {Roo.HtmlEditorCore} this
25561          * @param {String} html
25562          */
25563         beforesync: true,
25564          /**
25565          * @event beforepush
25566          * Fires before the iframe editor is updated with content from the textarea. Return false
25567          * to cancel the push.
25568          * @param {Roo.HtmlEditorCore} this
25569          * @param {String} html
25570          */
25571         beforepush: true,
25572          /**
25573          * @event sync
25574          * Fires when the textarea is updated with content from the editor iframe.
25575          * @param {Roo.HtmlEditorCore} this
25576          * @param {String} html
25577          */
25578         sync: true,
25579          /**
25580          * @event push
25581          * Fires when the iframe editor is updated with content from the textarea.
25582          * @param {Roo.HtmlEditorCore} this
25583          * @param {String} html
25584          */
25585         push: true,
25586         
25587         /**
25588          * @event editorevent
25589          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25590          * @param {Roo.HtmlEditorCore} this
25591          */
25592         editorevent: true
25593         
25594     });
25595     
25596     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25597     
25598     // defaults : white / black...
25599     this.applyBlacklists();
25600     
25601     
25602     
25603 };
25604
25605
25606 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25607
25608
25609      /**
25610      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25611      */
25612     
25613     owner : false,
25614     
25615      /**
25616      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25617      *                        Roo.resizable.
25618      */
25619     resizable : false,
25620      /**
25621      * @cfg {Number} height (in pixels)
25622      */   
25623     height: 300,
25624    /**
25625      * @cfg {Number} width (in pixels)
25626      */   
25627     width: 500,
25628     
25629     /**
25630      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25631      * 
25632      */
25633     stylesheets: false,
25634     
25635     /**
25636      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25637      */
25638     allowComments: false,
25639     // id of frame..
25640     frameId: false,
25641     
25642     // private properties
25643     validationEvent : false,
25644     deferHeight: true,
25645     initialized : false,
25646     activated : false,
25647     sourceEditMode : false,
25648     onFocus : Roo.emptyFn,
25649     iframePad:3,
25650     hideMode:'offsets',
25651     
25652     clearUp: true,
25653     
25654     // blacklist + whitelisted elements..
25655     black: false,
25656     white: false,
25657      
25658     bodyCls : '',
25659
25660     /**
25661      * Protected method that will not generally be called directly. It
25662      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25663      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25664      */
25665     getDocMarkup : function(){
25666         // body styles..
25667         var st = '';
25668         
25669         // inherit styels from page...?? 
25670         if (this.stylesheets === false) {
25671             
25672             Roo.get(document.head).select('style').each(function(node) {
25673                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25674             });
25675             
25676             Roo.get(document.head).select('link').each(function(node) { 
25677                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25678             });
25679             
25680         } else if (!this.stylesheets.length) {
25681                 // simple..
25682                 st = '<style type="text/css">' +
25683                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25684                    '</style>';
25685         } else {
25686             for (var i in this.stylesheets) { 
25687                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25688             }
25689             
25690         }
25691         
25692         st +=  '<style type="text/css">' +
25693             'IMG { cursor: pointer } ' +
25694         '</style>';
25695
25696         var cls = 'roo-htmleditor-body';
25697         
25698         if(this.bodyCls.length){
25699             cls += ' ' + this.bodyCls;
25700         }
25701         
25702         return '<html><head>' + st  +
25703             //<style type="text/css">' +
25704             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25705             //'</style>' +
25706             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25707     },
25708
25709     // private
25710     onRender : function(ct, position)
25711     {
25712         var _t = this;
25713         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25714         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25715         
25716         
25717         this.el.dom.style.border = '0 none';
25718         this.el.dom.setAttribute('tabIndex', -1);
25719         this.el.addClass('x-hidden hide');
25720         
25721         
25722         
25723         if(Roo.isIE){ // fix IE 1px bogus margin
25724             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25725         }
25726        
25727         
25728         this.frameId = Roo.id();
25729         
25730          
25731         
25732         var iframe = this.owner.wrap.createChild({
25733             tag: 'iframe',
25734             cls: 'form-control', // bootstrap..
25735             id: this.frameId,
25736             name: this.frameId,
25737             frameBorder : 'no',
25738             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25739         }, this.el
25740         );
25741         
25742         
25743         this.iframe = iframe.dom;
25744
25745          this.assignDocWin();
25746         
25747         this.doc.designMode = 'on';
25748        
25749         this.doc.open();
25750         this.doc.write(this.getDocMarkup());
25751         this.doc.close();
25752
25753         
25754         var task = { // must defer to wait for browser to be ready
25755             run : function(){
25756                 //console.log("run task?" + this.doc.readyState);
25757                 this.assignDocWin();
25758                 if(this.doc.body || this.doc.readyState == 'complete'){
25759                     try {
25760                         this.doc.designMode="on";
25761                     } catch (e) {
25762                         return;
25763                     }
25764                     Roo.TaskMgr.stop(task);
25765                     this.initEditor.defer(10, this);
25766                 }
25767             },
25768             interval : 10,
25769             duration: 10000,
25770             scope: this
25771         };
25772         Roo.TaskMgr.start(task);
25773
25774     },
25775
25776     // private
25777     onResize : function(w, h)
25778     {
25779          Roo.log('resize: ' +w + ',' + h );
25780         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25781         if(!this.iframe){
25782             return;
25783         }
25784         if(typeof w == 'number'){
25785             
25786             this.iframe.style.width = w + 'px';
25787         }
25788         if(typeof h == 'number'){
25789             
25790             this.iframe.style.height = h + 'px';
25791             if(this.doc){
25792                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25793             }
25794         }
25795         
25796     },
25797
25798     /**
25799      * Toggles the editor between standard and source edit mode.
25800      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25801      */
25802     toggleSourceEdit : function(sourceEditMode){
25803         
25804         this.sourceEditMode = sourceEditMode === true;
25805         
25806         if(this.sourceEditMode){
25807  
25808             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25809             
25810         }else{
25811             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25812             //this.iframe.className = '';
25813             this.deferFocus();
25814         }
25815         //this.setSize(this.owner.wrap.getSize());
25816         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25817     },
25818
25819     
25820   
25821
25822     /**
25823      * Protected method that will not generally be called directly. If you need/want
25824      * custom HTML cleanup, this is the method you should override.
25825      * @param {String} html The HTML to be cleaned
25826      * return {String} The cleaned HTML
25827      */
25828     cleanHtml : function(html){
25829         html = String(html);
25830         if(html.length > 5){
25831             if(Roo.isSafari){ // strip safari nonsense
25832                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25833             }
25834         }
25835         if(html == '&nbsp;'){
25836             html = '';
25837         }
25838         return html;
25839     },
25840
25841     /**
25842      * HTML Editor -> Textarea
25843      * Protected method that will not generally be called directly. Syncs the contents
25844      * of the editor iframe with the textarea.
25845      */
25846     syncValue : function(){
25847         if(this.initialized){
25848             var bd = (this.doc.body || this.doc.documentElement);
25849             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25850             var html = bd.innerHTML;
25851             if(Roo.isSafari){
25852                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25853                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25854                 if(m && m[1]){
25855                     html = '<div style="'+m[0]+'">' + html + '</div>';
25856                 }
25857             }
25858             html = this.cleanHtml(html);
25859             // fix up the special chars.. normaly like back quotes in word...
25860             // however we do not want to do this with chinese..
25861             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25862                 
25863                 var cc = match.charCodeAt();
25864
25865                 // Get the character value, handling surrogate pairs
25866                 if (match.length == 2) {
25867                     // It's a surrogate pair, calculate the Unicode code point
25868                     var high = match.charCodeAt(0) - 0xD800;
25869                     var low  = match.charCodeAt(1) - 0xDC00;
25870                     cc = (high * 0x400) + low + 0x10000;
25871                 }  else if (
25872                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25873                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25874                     (cc >= 0xf900 && cc < 0xfb00 )
25875                 ) {
25876                         return match;
25877                 }  
25878          
25879                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25880                 return "&#" + cc + ";";
25881                 
25882                 
25883             });
25884             
25885             
25886              
25887             if(this.owner.fireEvent('beforesync', this, html) !== false){
25888                 this.el.dom.value = html;
25889                 this.owner.fireEvent('sync', this, html);
25890             }
25891         }
25892     },
25893
25894     /**
25895      * Protected method that will not generally be called directly. Pushes the value of the textarea
25896      * into the iframe editor.
25897      */
25898     pushValue : function(){
25899         if(this.initialized){
25900             var v = this.el.dom.value.trim();
25901             
25902 //            if(v.length < 1){
25903 //                v = '&#160;';
25904 //            }
25905             
25906             if(this.owner.fireEvent('beforepush', this, v) !== false){
25907                 var d = (this.doc.body || this.doc.documentElement);
25908                 d.innerHTML = v;
25909                 this.cleanUpPaste();
25910                 this.el.dom.value = d.innerHTML;
25911                 this.owner.fireEvent('push', this, v);
25912             }
25913         }
25914     },
25915
25916     // private
25917     deferFocus : function(){
25918         this.focus.defer(10, this);
25919     },
25920
25921     // doc'ed in Field
25922     focus : function(){
25923         if(this.win && !this.sourceEditMode){
25924             this.win.focus();
25925         }else{
25926             this.el.focus();
25927         }
25928     },
25929     
25930     assignDocWin: function()
25931     {
25932         var iframe = this.iframe;
25933         
25934          if(Roo.isIE){
25935             this.doc = iframe.contentWindow.document;
25936             this.win = iframe.contentWindow;
25937         } else {
25938 //            if (!Roo.get(this.frameId)) {
25939 //                return;
25940 //            }
25941 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25942 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25943             
25944             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25945                 return;
25946             }
25947             
25948             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25949             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25950         }
25951     },
25952     
25953     // private
25954     initEditor : function(){
25955         //console.log("INIT EDITOR");
25956         this.assignDocWin();
25957         
25958         
25959         
25960         this.doc.designMode="on";
25961         this.doc.open();
25962         this.doc.write(this.getDocMarkup());
25963         this.doc.close();
25964         
25965         var dbody = (this.doc.body || this.doc.documentElement);
25966         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25967         // this copies styles from the containing element into thsi one..
25968         // not sure why we need all of this..
25969         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25970         
25971         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25972         //ss['background-attachment'] = 'fixed'; // w3c
25973         dbody.bgProperties = 'fixed'; // ie
25974         //Roo.DomHelper.applyStyles(dbody, ss);
25975         Roo.EventManager.on(this.doc, {
25976             //'mousedown': this.onEditorEvent,
25977             'mouseup': this.onEditorEvent,
25978             'dblclick': this.onEditorEvent,
25979             'click': this.onEditorEvent,
25980             'keyup': this.onEditorEvent,
25981             buffer:100,
25982             scope: this
25983         });
25984         if(Roo.isGecko){
25985             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25986         }
25987         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25988             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25989         }
25990         this.initialized = true;
25991
25992         this.owner.fireEvent('initialize', this);
25993         this.pushValue();
25994     },
25995
25996     // private
25997     onDestroy : function(){
25998         
25999         
26000         
26001         if(this.rendered){
26002             
26003             //for (var i =0; i < this.toolbars.length;i++) {
26004             //    // fixme - ask toolbars for heights?
26005             //    this.toolbars[i].onDestroy();
26006            // }
26007             
26008             //this.wrap.dom.innerHTML = '';
26009             //this.wrap.remove();
26010         }
26011     },
26012
26013     // private
26014     onFirstFocus : function(){
26015         
26016         this.assignDocWin();
26017         
26018         
26019         this.activated = true;
26020          
26021     
26022         if(Roo.isGecko){ // prevent silly gecko errors
26023             this.win.focus();
26024             var s = this.win.getSelection();
26025             if(!s.focusNode || s.focusNode.nodeType != 3){
26026                 var r = s.getRangeAt(0);
26027                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26028                 r.collapse(true);
26029                 this.deferFocus();
26030             }
26031             try{
26032                 this.execCmd('useCSS', true);
26033                 this.execCmd('styleWithCSS', false);
26034             }catch(e){}
26035         }
26036         this.owner.fireEvent('activate', this);
26037     },
26038
26039     // private
26040     adjustFont: function(btn){
26041         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26042         //if(Roo.isSafari){ // safari
26043         //    adjust *= 2;
26044        // }
26045         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26046         if(Roo.isSafari){ // safari
26047             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26048             v =  (v < 10) ? 10 : v;
26049             v =  (v > 48) ? 48 : v;
26050             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26051             
26052         }
26053         
26054         
26055         v = Math.max(1, v+adjust);
26056         
26057         this.execCmd('FontSize', v  );
26058     },
26059
26060     onEditorEvent : function(e)
26061     {
26062         this.owner.fireEvent('editorevent', this, e);
26063       //  this.updateToolbar();
26064         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26065     },
26066
26067     insertTag : function(tg)
26068     {
26069         // could be a bit smarter... -> wrap the current selected tRoo..
26070         if (tg.toLowerCase() == 'span' ||
26071             tg.toLowerCase() == 'code' ||
26072             tg.toLowerCase() == 'sup' ||
26073             tg.toLowerCase() == 'sub' 
26074             ) {
26075             
26076             range = this.createRange(this.getSelection());
26077             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26078             wrappingNode.appendChild(range.extractContents());
26079             range.insertNode(wrappingNode);
26080
26081             return;
26082             
26083             
26084             
26085         }
26086         this.execCmd("formatblock",   tg);
26087         
26088     },
26089     
26090     insertText : function(txt)
26091     {
26092         
26093         
26094         var range = this.createRange();
26095         range.deleteContents();
26096                //alert(Sender.getAttribute('label'));
26097                
26098         range.insertNode(this.doc.createTextNode(txt));
26099     } ,
26100     
26101      
26102
26103     /**
26104      * Executes a Midas editor command on the editor document and performs necessary focus and
26105      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26106      * @param {String} cmd The Midas command
26107      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26108      */
26109     relayCmd : function(cmd, value){
26110         this.win.focus();
26111         this.execCmd(cmd, value);
26112         this.owner.fireEvent('editorevent', this);
26113         //this.updateToolbar();
26114         this.owner.deferFocus();
26115     },
26116
26117     /**
26118      * Executes a Midas editor command directly on the editor document.
26119      * For visual commands, you should use {@link #relayCmd} instead.
26120      * <b>This should only be called after the editor is initialized.</b>
26121      * @param {String} cmd The Midas command
26122      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26123      */
26124     execCmd : function(cmd, value){
26125         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26126         this.syncValue();
26127     },
26128  
26129  
26130    
26131     /**
26132      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26133      * to insert tRoo.
26134      * @param {String} text | dom node.. 
26135      */
26136     insertAtCursor : function(text)
26137     {
26138         
26139         if(!this.activated){
26140             return;
26141         }
26142         /*
26143         if(Roo.isIE){
26144             this.win.focus();
26145             var r = this.doc.selection.createRange();
26146             if(r){
26147                 r.collapse(true);
26148                 r.pasteHTML(text);
26149                 this.syncValue();
26150                 this.deferFocus();
26151             
26152             }
26153             return;
26154         }
26155         */
26156         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26157             this.win.focus();
26158             
26159             
26160             // from jquery ui (MIT licenced)
26161             var range, node;
26162             var win = this.win;
26163             
26164             if (win.getSelection && win.getSelection().getRangeAt) {
26165                 range = win.getSelection().getRangeAt(0);
26166                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26167                 range.insertNode(node);
26168             } else if (win.document.selection && win.document.selection.createRange) {
26169                 // no firefox support
26170                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26171                 win.document.selection.createRange().pasteHTML(txt);
26172             } else {
26173                 // no firefox support
26174                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26175                 this.execCmd('InsertHTML', txt);
26176             } 
26177             
26178             this.syncValue();
26179             
26180             this.deferFocus();
26181         }
26182     },
26183  // private
26184     mozKeyPress : function(e){
26185         if(e.ctrlKey){
26186             var c = e.getCharCode(), cmd;
26187           
26188             if(c > 0){
26189                 c = String.fromCharCode(c).toLowerCase();
26190                 switch(c){
26191                     case 'b':
26192                         cmd = 'bold';
26193                         break;
26194                     case 'i':
26195                         cmd = 'italic';
26196                         break;
26197                     
26198                     case 'u':
26199                         cmd = 'underline';
26200                         break;
26201                     
26202                     case 'v':
26203                         this.cleanUpPaste.defer(100, this);
26204                         return;
26205                         
26206                 }
26207                 if(cmd){
26208                     this.win.focus();
26209                     this.execCmd(cmd);
26210                     this.deferFocus();
26211                     e.preventDefault();
26212                 }
26213                 
26214             }
26215         }
26216     },
26217
26218     // private
26219     fixKeys : function(){ // load time branching for fastest keydown performance
26220         if(Roo.isIE){
26221             return function(e){
26222                 var k = e.getKey(), r;
26223                 if(k == e.TAB){
26224                     e.stopEvent();
26225                     r = this.doc.selection.createRange();
26226                     if(r){
26227                         r.collapse(true);
26228                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26229                         this.deferFocus();
26230                     }
26231                     return;
26232                 }
26233                 
26234                 if(k == e.ENTER){
26235                     r = this.doc.selection.createRange();
26236                     if(r){
26237                         var target = r.parentElement();
26238                         if(!target || target.tagName.toLowerCase() != 'li'){
26239                             e.stopEvent();
26240                             r.pasteHTML('<br />');
26241                             r.collapse(false);
26242                             r.select();
26243                         }
26244                     }
26245                 }
26246                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26247                     this.cleanUpPaste.defer(100, this);
26248                     return;
26249                 }
26250                 
26251                 
26252             };
26253         }else if(Roo.isOpera){
26254             return function(e){
26255                 var k = e.getKey();
26256                 if(k == e.TAB){
26257                     e.stopEvent();
26258                     this.win.focus();
26259                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26260                     this.deferFocus();
26261                 }
26262                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26263                     this.cleanUpPaste.defer(100, this);
26264                     return;
26265                 }
26266                 
26267             };
26268         }else if(Roo.isSafari){
26269             return function(e){
26270                 var k = e.getKey();
26271                 
26272                 if(k == e.TAB){
26273                     e.stopEvent();
26274                     this.execCmd('InsertText','\t');
26275                     this.deferFocus();
26276                     return;
26277                 }
26278                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26279                     this.cleanUpPaste.defer(100, this);
26280                     return;
26281                 }
26282                 
26283              };
26284         }
26285     }(),
26286     
26287     getAllAncestors: function()
26288     {
26289         var p = this.getSelectedNode();
26290         var a = [];
26291         if (!p) {
26292             a.push(p); // push blank onto stack..
26293             p = this.getParentElement();
26294         }
26295         
26296         
26297         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26298             a.push(p);
26299             p = p.parentNode;
26300         }
26301         a.push(this.doc.body);
26302         return a;
26303     },
26304     lastSel : false,
26305     lastSelNode : false,
26306     
26307     
26308     getSelection : function() 
26309     {
26310         this.assignDocWin();
26311         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26312     },
26313     
26314     getSelectedNode: function() 
26315     {
26316         // this may only work on Gecko!!!
26317         
26318         // should we cache this!!!!
26319         
26320         
26321         
26322          
26323         var range = this.createRange(this.getSelection()).cloneRange();
26324         
26325         if (Roo.isIE) {
26326             var parent = range.parentElement();
26327             while (true) {
26328                 var testRange = range.duplicate();
26329                 testRange.moveToElementText(parent);
26330                 if (testRange.inRange(range)) {
26331                     break;
26332                 }
26333                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26334                     break;
26335                 }
26336                 parent = parent.parentElement;
26337             }
26338             return parent;
26339         }
26340         
26341         // is ancestor a text element.
26342         var ac =  range.commonAncestorContainer;
26343         if (ac.nodeType == 3) {
26344             ac = ac.parentNode;
26345         }
26346         
26347         var ar = ac.childNodes;
26348          
26349         var nodes = [];
26350         var other_nodes = [];
26351         var has_other_nodes = false;
26352         for (var i=0;i<ar.length;i++) {
26353             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26354                 continue;
26355             }
26356             // fullly contained node.
26357             
26358             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26359                 nodes.push(ar[i]);
26360                 continue;
26361             }
26362             
26363             // probably selected..
26364             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26365                 other_nodes.push(ar[i]);
26366                 continue;
26367             }
26368             // outer..
26369             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26370                 continue;
26371             }
26372             
26373             
26374             has_other_nodes = true;
26375         }
26376         if (!nodes.length && other_nodes.length) {
26377             nodes= other_nodes;
26378         }
26379         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26380             return false;
26381         }
26382         
26383         return nodes[0];
26384     },
26385     createRange: function(sel)
26386     {
26387         // this has strange effects when using with 
26388         // top toolbar - not sure if it's a great idea.
26389         //this.editor.contentWindow.focus();
26390         if (typeof sel != "undefined") {
26391             try {
26392                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26393             } catch(e) {
26394                 return this.doc.createRange();
26395             }
26396         } else {
26397             return this.doc.createRange();
26398         }
26399     },
26400     getParentElement: function()
26401     {
26402         
26403         this.assignDocWin();
26404         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26405         
26406         var range = this.createRange(sel);
26407          
26408         try {
26409             var p = range.commonAncestorContainer;
26410             while (p.nodeType == 3) { // text node
26411                 p = p.parentNode;
26412             }
26413             return p;
26414         } catch (e) {
26415             return null;
26416         }
26417     
26418     },
26419     /***
26420      *
26421      * Range intersection.. the hard stuff...
26422      *  '-1' = before
26423      *  '0' = hits..
26424      *  '1' = after.
26425      *         [ -- selected range --- ]
26426      *   [fail]                        [fail]
26427      *
26428      *    basically..
26429      *      if end is before start or  hits it. fail.
26430      *      if start is after end or hits it fail.
26431      *
26432      *   if either hits (but other is outside. - then it's not 
26433      *   
26434      *    
26435      **/
26436     
26437     
26438     // @see http://www.thismuchiknow.co.uk/?p=64.
26439     rangeIntersectsNode : function(range, node)
26440     {
26441         var nodeRange = node.ownerDocument.createRange();
26442         try {
26443             nodeRange.selectNode(node);
26444         } catch (e) {
26445             nodeRange.selectNodeContents(node);
26446         }
26447     
26448         var rangeStartRange = range.cloneRange();
26449         rangeStartRange.collapse(true);
26450     
26451         var rangeEndRange = range.cloneRange();
26452         rangeEndRange.collapse(false);
26453     
26454         var nodeStartRange = nodeRange.cloneRange();
26455         nodeStartRange.collapse(true);
26456     
26457         var nodeEndRange = nodeRange.cloneRange();
26458         nodeEndRange.collapse(false);
26459     
26460         return rangeStartRange.compareBoundaryPoints(
26461                  Range.START_TO_START, nodeEndRange) == -1 &&
26462                rangeEndRange.compareBoundaryPoints(
26463                  Range.START_TO_START, nodeStartRange) == 1;
26464         
26465          
26466     },
26467     rangeCompareNode : function(range, node)
26468     {
26469         var nodeRange = node.ownerDocument.createRange();
26470         try {
26471             nodeRange.selectNode(node);
26472         } catch (e) {
26473             nodeRange.selectNodeContents(node);
26474         }
26475         
26476         
26477         range.collapse(true);
26478     
26479         nodeRange.collapse(true);
26480      
26481         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26482         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26483          
26484         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26485         
26486         var nodeIsBefore   =  ss == 1;
26487         var nodeIsAfter    = ee == -1;
26488         
26489         if (nodeIsBefore && nodeIsAfter) {
26490             return 0; // outer
26491         }
26492         if (!nodeIsBefore && nodeIsAfter) {
26493             return 1; //right trailed.
26494         }
26495         
26496         if (nodeIsBefore && !nodeIsAfter) {
26497             return 2;  // left trailed.
26498         }
26499         // fully contined.
26500         return 3;
26501     },
26502
26503     // private? - in a new class?
26504     cleanUpPaste :  function()
26505     {
26506         // cleans up the whole document..
26507         Roo.log('cleanuppaste');
26508         
26509         this.cleanUpChildren(this.doc.body);
26510         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26511         if (clean != this.doc.body.innerHTML) {
26512             this.doc.body.innerHTML = clean;
26513         }
26514         
26515     },
26516     
26517     cleanWordChars : function(input) {// change the chars to hex code
26518         var he = Roo.HtmlEditorCore;
26519         
26520         var output = input;
26521         Roo.each(he.swapCodes, function(sw) { 
26522             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26523             
26524             output = output.replace(swapper, sw[1]);
26525         });
26526         
26527         return output;
26528     },
26529     
26530     
26531     cleanUpChildren : function (n)
26532     {
26533         if (!n.childNodes.length) {
26534             return;
26535         }
26536         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26537            this.cleanUpChild(n.childNodes[i]);
26538         }
26539     },
26540     
26541     
26542         
26543     
26544     cleanUpChild : function (node)
26545     {
26546         var ed = this;
26547         //console.log(node);
26548         if (node.nodeName == "#text") {
26549             // clean up silly Windows -- stuff?
26550             return; 
26551         }
26552         if (node.nodeName == "#comment") {
26553             if (!this.allowComments) {
26554                 node.parentNode.removeChild(node);
26555             }
26556             // clean up silly Windows -- stuff?
26557             return; 
26558         }
26559         var lcname = node.tagName.toLowerCase();
26560         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26561         // whitelist of tags..
26562         
26563         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26564             // remove node.
26565             node.parentNode.removeChild(node);
26566             return;
26567             
26568         }
26569         
26570         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26571         
26572         // spans with no attributes - just remove them..
26573         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26574             remove_keep_children = true;
26575         }
26576         
26577         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26578         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26579         
26580         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26581         //    remove_keep_children = true;
26582         //}
26583         
26584         if (remove_keep_children) {
26585             this.cleanUpChildren(node);
26586             // inserts everything just before this node...
26587             while (node.childNodes.length) {
26588                 var cn = node.childNodes[0];
26589                 node.removeChild(cn);
26590                 node.parentNode.insertBefore(cn, node);
26591             }
26592             node.parentNode.removeChild(node);
26593             return;
26594         }
26595         
26596         if (!node.attributes || !node.attributes.length) {
26597             
26598           
26599             
26600             
26601             this.cleanUpChildren(node);
26602             return;
26603         }
26604         
26605         function cleanAttr(n,v)
26606         {
26607             
26608             if (v.match(/^\./) || v.match(/^\//)) {
26609                 return;
26610             }
26611             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26612                 return;
26613             }
26614             if (v.match(/^#/)) {
26615                 return;
26616             }
26617             if (v.match(/^\{/)) { // allow template editing.
26618                 return;
26619             }
26620 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26621             node.removeAttribute(n);
26622             
26623         }
26624         
26625         var cwhite = this.cwhite;
26626         var cblack = this.cblack;
26627             
26628         function cleanStyle(n,v)
26629         {
26630             if (v.match(/expression/)) { //XSS?? should we even bother..
26631                 node.removeAttribute(n);
26632                 return;
26633             }
26634             
26635             var parts = v.split(/;/);
26636             var clean = [];
26637             
26638             Roo.each(parts, function(p) {
26639                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26640                 if (!p.length) {
26641                     return true;
26642                 }
26643                 var l = p.split(':').shift().replace(/\s+/g,'');
26644                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26645                 
26646                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26647 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26648                     //node.removeAttribute(n);
26649                     return true;
26650                 }
26651                 //Roo.log()
26652                 // only allow 'c whitelisted system attributes'
26653                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26654 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26655                     //node.removeAttribute(n);
26656                     return true;
26657                 }
26658                 
26659                 
26660                  
26661                 
26662                 clean.push(p);
26663                 return true;
26664             });
26665             if (clean.length) { 
26666                 node.setAttribute(n, clean.join(';'));
26667             } else {
26668                 node.removeAttribute(n);
26669             }
26670             
26671         }
26672         
26673         
26674         for (var i = node.attributes.length-1; i > -1 ; i--) {
26675             var a = node.attributes[i];
26676             //console.log(a);
26677             
26678             if (a.name.toLowerCase().substr(0,2)=='on')  {
26679                 node.removeAttribute(a.name);
26680                 continue;
26681             }
26682             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26683                 node.removeAttribute(a.name);
26684                 continue;
26685             }
26686             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26687                 cleanAttr(a.name,a.value); // fixme..
26688                 continue;
26689             }
26690             if (a.name == 'style') {
26691                 cleanStyle(a.name,a.value);
26692                 continue;
26693             }
26694             /// clean up MS crap..
26695             // tecnically this should be a list of valid class'es..
26696             
26697             
26698             if (a.name == 'class') {
26699                 if (a.value.match(/^Mso/)) {
26700                     node.removeAttribute('class');
26701                 }
26702                 
26703                 if (a.value.match(/^body$/)) {
26704                     node.removeAttribute('class');
26705                 }
26706                 continue;
26707             }
26708             
26709             // style cleanup!?
26710             // class cleanup?
26711             
26712         }
26713         
26714         
26715         this.cleanUpChildren(node);
26716         
26717         
26718     },
26719     
26720     /**
26721      * Clean up MS wordisms...
26722      */
26723     cleanWord : function(node)
26724     {
26725         if (!node) {
26726             this.cleanWord(this.doc.body);
26727             return;
26728         }
26729         
26730         if(
26731                 node.nodeName == 'SPAN' &&
26732                 !node.hasAttributes() &&
26733                 node.childNodes.length == 1 &&
26734                 node.firstChild.nodeName == "#text"  
26735         ) {
26736             var textNode = node.firstChild;
26737             node.removeChild(textNode);
26738             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26739                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26740             }
26741             node.parentNode.insertBefore(textNode, node);
26742             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26743                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26744             }
26745             node.parentNode.removeChild(node);
26746         }
26747         
26748         if (node.nodeName == "#text") {
26749             // clean up silly Windows -- stuff?
26750             return; 
26751         }
26752         if (node.nodeName == "#comment") {
26753             node.parentNode.removeChild(node);
26754             // clean up silly Windows -- stuff?
26755             return; 
26756         }
26757         
26758         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26759             node.parentNode.removeChild(node);
26760             return;
26761         }
26762         //Roo.log(node.tagName);
26763         // remove - but keep children..
26764         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26765             //Roo.log('-- removed');
26766             while (node.childNodes.length) {
26767                 var cn = node.childNodes[0];
26768                 node.removeChild(cn);
26769                 node.parentNode.insertBefore(cn, node);
26770                 // move node to parent - and clean it..
26771                 this.cleanWord(cn);
26772             }
26773             node.parentNode.removeChild(node);
26774             /// no need to iterate chidlren = it's got none..
26775             //this.iterateChildren(node, this.cleanWord);
26776             return;
26777         }
26778         // clean styles
26779         if (node.className.length) {
26780             
26781             var cn = node.className.split(/\W+/);
26782             var cna = [];
26783             Roo.each(cn, function(cls) {
26784                 if (cls.match(/Mso[a-zA-Z]+/)) {
26785                     return;
26786                 }
26787                 cna.push(cls);
26788             });
26789             node.className = cna.length ? cna.join(' ') : '';
26790             if (!cna.length) {
26791                 node.removeAttribute("class");
26792             }
26793         }
26794         
26795         if (node.hasAttribute("lang")) {
26796             node.removeAttribute("lang");
26797         }
26798         
26799         if (node.hasAttribute("style")) {
26800             
26801             var styles = node.getAttribute("style").split(";");
26802             var nstyle = [];
26803             Roo.each(styles, function(s) {
26804                 if (!s.match(/:/)) {
26805                     return;
26806                 }
26807                 var kv = s.split(":");
26808                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26809                     return;
26810                 }
26811                 // what ever is left... we allow.
26812                 nstyle.push(s);
26813             });
26814             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26815             if (!nstyle.length) {
26816                 node.removeAttribute('style');
26817             }
26818         }
26819         this.iterateChildren(node, this.cleanWord);
26820         
26821         
26822         
26823     },
26824     /**
26825      * iterateChildren of a Node, calling fn each time, using this as the scole..
26826      * @param {DomNode} node node to iterate children of.
26827      * @param {Function} fn method of this class to call on each item.
26828      */
26829     iterateChildren : function(node, fn)
26830     {
26831         if (!node.childNodes.length) {
26832                 return;
26833         }
26834         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26835            fn.call(this, node.childNodes[i])
26836         }
26837     },
26838     
26839     
26840     /**
26841      * cleanTableWidths.
26842      *
26843      * Quite often pasting from word etc.. results in tables with column and widths.
26844      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26845      *
26846      */
26847     cleanTableWidths : function(node)
26848     {
26849          
26850          
26851         if (!node) {
26852             this.cleanTableWidths(this.doc.body);
26853             return;
26854         }
26855         
26856         // ignore list...
26857         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26858             return; 
26859         }
26860         Roo.log(node.tagName);
26861         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26862             this.iterateChildren(node, this.cleanTableWidths);
26863             return;
26864         }
26865         if (node.hasAttribute('width')) {
26866             node.removeAttribute('width');
26867         }
26868         
26869          
26870         if (node.hasAttribute("style")) {
26871             // pretty basic...
26872             
26873             var styles = node.getAttribute("style").split(";");
26874             var nstyle = [];
26875             Roo.each(styles, function(s) {
26876                 if (!s.match(/:/)) {
26877                     return;
26878                 }
26879                 var kv = s.split(":");
26880                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26881                     return;
26882                 }
26883                 // what ever is left... we allow.
26884                 nstyle.push(s);
26885             });
26886             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26887             if (!nstyle.length) {
26888                 node.removeAttribute('style');
26889             }
26890         }
26891         
26892         this.iterateChildren(node, this.cleanTableWidths);
26893         
26894         
26895     },
26896     
26897     
26898     
26899     
26900     domToHTML : function(currentElement, depth, nopadtext) {
26901         
26902         depth = depth || 0;
26903         nopadtext = nopadtext || false;
26904     
26905         if (!currentElement) {
26906             return this.domToHTML(this.doc.body);
26907         }
26908         
26909         //Roo.log(currentElement);
26910         var j;
26911         var allText = false;
26912         var nodeName = currentElement.nodeName;
26913         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26914         
26915         if  (nodeName == '#text') {
26916             
26917             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26918         }
26919         
26920         
26921         var ret = '';
26922         if (nodeName != 'BODY') {
26923              
26924             var i = 0;
26925             // Prints the node tagName, such as <A>, <IMG>, etc
26926             if (tagName) {
26927                 var attr = [];
26928                 for(i = 0; i < currentElement.attributes.length;i++) {
26929                     // quoting?
26930                     var aname = currentElement.attributes.item(i).name;
26931                     if (!currentElement.attributes.item(i).value.length) {
26932                         continue;
26933                     }
26934                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26935                 }
26936                 
26937                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26938             } 
26939             else {
26940                 
26941                 // eack
26942             }
26943         } else {
26944             tagName = false;
26945         }
26946         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26947             return ret;
26948         }
26949         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26950             nopadtext = true;
26951         }
26952         
26953         
26954         // Traverse the tree
26955         i = 0;
26956         var currentElementChild = currentElement.childNodes.item(i);
26957         var allText = true;
26958         var innerHTML  = '';
26959         lastnode = '';
26960         while (currentElementChild) {
26961             // Formatting code (indent the tree so it looks nice on the screen)
26962             var nopad = nopadtext;
26963             if (lastnode == 'SPAN') {
26964                 nopad  = true;
26965             }
26966             // text
26967             if  (currentElementChild.nodeName == '#text') {
26968                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26969                 toadd = nopadtext ? toadd : toadd.trim();
26970                 if (!nopad && toadd.length > 80) {
26971                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26972                 }
26973                 innerHTML  += toadd;
26974                 
26975                 i++;
26976                 currentElementChild = currentElement.childNodes.item(i);
26977                 lastNode = '';
26978                 continue;
26979             }
26980             allText = false;
26981             
26982             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26983                 
26984             // Recursively traverse the tree structure of the child node
26985             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26986             lastnode = currentElementChild.nodeName;
26987             i++;
26988             currentElementChild=currentElement.childNodes.item(i);
26989         }
26990         
26991         ret += innerHTML;
26992         
26993         if (!allText) {
26994                 // The remaining code is mostly for formatting the tree
26995             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26996         }
26997         
26998         
26999         if (tagName) {
27000             ret+= "</"+tagName+">";
27001         }
27002         return ret;
27003         
27004     },
27005         
27006     applyBlacklists : function()
27007     {
27008         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27009         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27010         
27011         this.white = [];
27012         this.black = [];
27013         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27014             if (b.indexOf(tag) > -1) {
27015                 return;
27016             }
27017             this.white.push(tag);
27018             
27019         }, this);
27020         
27021         Roo.each(w, function(tag) {
27022             if (b.indexOf(tag) > -1) {
27023                 return;
27024             }
27025             if (this.white.indexOf(tag) > -1) {
27026                 return;
27027             }
27028             this.white.push(tag);
27029             
27030         }, this);
27031         
27032         
27033         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27034             if (w.indexOf(tag) > -1) {
27035                 return;
27036             }
27037             this.black.push(tag);
27038             
27039         }, this);
27040         
27041         Roo.each(b, function(tag) {
27042             if (w.indexOf(tag) > -1) {
27043                 return;
27044             }
27045             if (this.black.indexOf(tag) > -1) {
27046                 return;
27047             }
27048             this.black.push(tag);
27049             
27050         }, this);
27051         
27052         
27053         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27054         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27055         
27056         this.cwhite = [];
27057         this.cblack = [];
27058         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27059             if (b.indexOf(tag) > -1) {
27060                 return;
27061             }
27062             this.cwhite.push(tag);
27063             
27064         }, this);
27065         
27066         Roo.each(w, function(tag) {
27067             if (b.indexOf(tag) > -1) {
27068                 return;
27069             }
27070             if (this.cwhite.indexOf(tag) > -1) {
27071                 return;
27072             }
27073             this.cwhite.push(tag);
27074             
27075         }, this);
27076         
27077         
27078         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27079             if (w.indexOf(tag) > -1) {
27080                 return;
27081             }
27082             this.cblack.push(tag);
27083             
27084         }, this);
27085         
27086         Roo.each(b, function(tag) {
27087             if (w.indexOf(tag) > -1) {
27088                 return;
27089             }
27090             if (this.cblack.indexOf(tag) > -1) {
27091                 return;
27092             }
27093             this.cblack.push(tag);
27094             
27095         }, this);
27096     },
27097     
27098     setStylesheets : function(stylesheets)
27099     {
27100         if(typeof(stylesheets) == 'string'){
27101             Roo.get(this.iframe.contentDocument.head).createChild({
27102                 tag : 'link',
27103                 rel : 'stylesheet',
27104                 type : 'text/css',
27105                 href : stylesheets
27106             });
27107             
27108             return;
27109         }
27110         var _this = this;
27111      
27112         Roo.each(stylesheets, function(s) {
27113             if(!s.length){
27114                 return;
27115             }
27116             
27117             Roo.get(_this.iframe.contentDocument.head).createChild({
27118                 tag : 'link',
27119                 rel : 'stylesheet',
27120                 type : 'text/css',
27121                 href : s
27122             });
27123         });
27124
27125         
27126     },
27127     
27128     removeStylesheets : function()
27129     {
27130         var _this = this;
27131         
27132         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27133             s.remove();
27134         });
27135     },
27136     
27137     setStyle : function(style)
27138     {
27139         Roo.get(this.iframe.contentDocument.head).createChild({
27140             tag : 'style',
27141             type : 'text/css',
27142             html : style
27143         });
27144
27145         return;
27146     }
27147     
27148     // hide stuff that is not compatible
27149     /**
27150      * @event blur
27151      * @hide
27152      */
27153     /**
27154      * @event change
27155      * @hide
27156      */
27157     /**
27158      * @event focus
27159      * @hide
27160      */
27161     /**
27162      * @event specialkey
27163      * @hide
27164      */
27165     /**
27166      * @cfg {String} fieldClass @hide
27167      */
27168     /**
27169      * @cfg {String} focusClass @hide
27170      */
27171     /**
27172      * @cfg {String} autoCreate @hide
27173      */
27174     /**
27175      * @cfg {String} inputType @hide
27176      */
27177     /**
27178      * @cfg {String} invalidClass @hide
27179      */
27180     /**
27181      * @cfg {String} invalidText @hide
27182      */
27183     /**
27184      * @cfg {String} msgFx @hide
27185      */
27186     /**
27187      * @cfg {String} validateOnBlur @hide
27188      */
27189 });
27190
27191 Roo.HtmlEditorCore.white = [
27192         'area', 'br', 'img', 'input', 'hr', 'wbr',
27193         
27194        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27195        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27196        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27197        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27198        'table',   'ul',         'xmp', 
27199        
27200        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27201       'thead',   'tr', 
27202      
27203       'dir', 'menu', 'ol', 'ul', 'dl',
27204        
27205       'embed',  'object'
27206 ];
27207
27208
27209 Roo.HtmlEditorCore.black = [
27210     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27211         'applet', // 
27212         'base',   'basefont', 'bgsound', 'blink',  'body', 
27213         'frame',  'frameset', 'head',    'html',   'ilayer', 
27214         'iframe', 'layer',  'link',     'meta',    'object',   
27215         'script', 'style' ,'title',  'xml' // clean later..
27216 ];
27217 Roo.HtmlEditorCore.clean = [
27218     'script', 'style', 'title', 'xml'
27219 ];
27220 Roo.HtmlEditorCore.remove = [
27221     'font'
27222 ];
27223 // attributes..
27224
27225 Roo.HtmlEditorCore.ablack = [
27226     'on'
27227 ];
27228     
27229 Roo.HtmlEditorCore.aclean = [ 
27230     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27231 ];
27232
27233 // protocols..
27234 Roo.HtmlEditorCore.pwhite= [
27235         'http',  'https',  'mailto'
27236 ];
27237
27238 // white listed style attributes.
27239 Roo.HtmlEditorCore.cwhite= [
27240       //  'text-align', /// default is to allow most things..
27241       
27242          
27243 //        'font-size'//??
27244 ];
27245
27246 // black listed style attributes.
27247 Roo.HtmlEditorCore.cblack= [
27248       //  'font-size' -- this can be set by the project 
27249 ];
27250
27251
27252 Roo.HtmlEditorCore.swapCodes   =[ 
27253     [    8211, "&#8211;" ], 
27254     [    8212, "&#8212;" ], 
27255     [    8216,  "'" ],  
27256     [    8217, "'" ],  
27257     [    8220, '"' ],  
27258     [    8221, '"' ],  
27259     [    8226, "*" ],  
27260     [    8230, "..." ]
27261 ]; 
27262
27263     /*
27264  * - LGPL
27265  *
27266  * HtmlEditor
27267  * 
27268  */
27269
27270 /**
27271  * @class Roo.bootstrap.HtmlEditor
27272  * @extends Roo.bootstrap.TextArea
27273  * Bootstrap HtmlEditor class
27274
27275  * @constructor
27276  * Create a new HtmlEditor
27277  * @param {Object} config The config object
27278  */
27279
27280 Roo.bootstrap.HtmlEditor = function(config){
27281     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27282     if (!this.toolbars) {
27283         this.toolbars = [];
27284     }
27285     
27286     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27287     this.addEvents({
27288             /**
27289              * @event initialize
27290              * Fires when the editor is fully initialized (including the iframe)
27291              * @param {HtmlEditor} this
27292              */
27293             initialize: true,
27294             /**
27295              * @event activate
27296              * Fires when the editor is first receives the focus. Any insertion must wait
27297              * until after this event.
27298              * @param {HtmlEditor} this
27299              */
27300             activate: true,
27301              /**
27302              * @event beforesync
27303              * Fires before the textarea is updated with content from the editor iframe. Return false
27304              * to cancel the sync.
27305              * @param {HtmlEditor} this
27306              * @param {String} html
27307              */
27308             beforesync: true,
27309              /**
27310              * @event beforepush
27311              * Fires before the iframe editor is updated with content from the textarea. Return false
27312              * to cancel the push.
27313              * @param {HtmlEditor} this
27314              * @param {String} html
27315              */
27316             beforepush: true,
27317              /**
27318              * @event sync
27319              * Fires when the textarea is updated with content from the editor iframe.
27320              * @param {HtmlEditor} this
27321              * @param {String} html
27322              */
27323             sync: true,
27324              /**
27325              * @event push
27326              * Fires when the iframe editor is updated with content from the textarea.
27327              * @param {HtmlEditor} this
27328              * @param {String} html
27329              */
27330             push: true,
27331              /**
27332              * @event editmodechange
27333              * Fires when the editor switches edit modes
27334              * @param {HtmlEditor} this
27335              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27336              */
27337             editmodechange: true,
27338             /**
27339              * @event editorevent
27340              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27341              * @param {HtmlEditor} this
27342              */
27343             editorevent: true,
27344             /**
27345              * @event firstfocus
27346              * Fires when on first focus - needed by toolbars..
27347              * @param {HtmlEditor} this
27348              */
27349             firstfocus: true,
27350             /**
27351              * @event autosave
27352              * Auto save the htmlEditor value as a file into Events
27353              * @param {HtmlEditor} this
27354              */
27355             autosave: true,
27356             /**
27357              * @event savedpreview
27358              * preview the saved version of htmlEditor
27359              * @param {HtmlEditor} this
27360              */
27361             savedpreview: true
27362         });
27363 };
27364
27365
27366 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27367     
27368     
27369       /**
27370      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27371      */
27372     toolbars : false,
27373     
27374      /**
27375     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27376     */
27377     btns : [],
27378    
27379      /**
27380      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27381      *                        Roo.resizable.
27382      */
27383     resizable : false,
27384      /**
27385      * @cfg {Number} height (in pixels)
27386      */   
27387     height: 300,
27388    /**
27389      * @cfg {Number} width (in pixels)
27390      */   
27391     width: false,
27392     
27393     /**
27394      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27395      * 
27396      */
27397     stylesheets: false,
27398     
27399     // id of frame..
27400     frameId: false,
27401     
27402     // private properties
27403     validationEvent : false,
27404     deferHeight: true,
27405     initialized : false,
27406     activated : false,
27407     
27408     onFocus : Roo.emptyFn,
27409     iframePad:3,
27410     hideMode:'offsets',
27411     
27412     tbContainer : false,
27413     
27414     bodyCls : '',
27415     
27416     toolbarContainer :function() {
27417         return this.wrap.select('.x-html-editor-tb',true).first();
27418     },
27419
27420     /**
27421      * Protected method that will not generally be called directly. It
27422      * is called when the editor creates its toolbar. Override this method if you need to
27423      * add custom toolbar buttons.
27424      * @param {HtmlEditor} editor
27425      */
27426     createToolbar : function(){
27427         Roo.log('renewing');
27428         Roo.log("create toolbars");
27429         
27430         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27431         this.toolbars[0].render(this.toolbarContainer());
27432         
27433         return;
27434         
27435 //        if (!editor.toolbars || !editor.toolbars.length) {
27436 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27437 //        }
27438 //        
27439 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27440 //            editor.toolbars[i] = Roo.factory(
27441 //                    typeof(editor.toolbars[i]) == 'string' ?
27442 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27443 //                Roo.bootstrap.HtmlEditor);
27444 //            editor.toolbars[i].init(editor);
27445 //        }
27446     },
27447
27448      
27449     // private
27450     onRender : function(ct, position)
27451     {
27452        // Roo.log("Call onRender: " + this.xtype);
27453         var _t = this;
27454         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27455       
27456         this.wrap = this.inputEl().wrap({
27457             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27458         });
27459         
27460         this.editorcore.onRender(ct, position);
27461          
27462         if (this.resizable) {
27463             this.resizeEl = new Roo.Resizable(this.wrap, {
27464                 pinned : true,
27465                 wrap: true,
27466                 dynamic : true,
27467                 minHeight : this.height,
27468                 height: this.height,
27469                 handles : this.resizable,
27470                 width: this.width,
27471                 listeners : {
27472                     resize : function(r, w, h) {
27473                         _t.onResize(w,h); // -something
27474                     }
27475                 }
27476             });
27477             
27478         }
27479         this.createToolbar(this);
27480        
27481         
27482         if(!this.width && this.resizable){
27483             this.setSize(this.wrap.getSize());
27484         }
27485         if (this.resizeEl) {
27486             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27487             // should trigger onReize..
27488         }
27489         
27490     },
27491
27492     // private
27493     onResize : function(w, h)
27494     {
27495         Roo.log('resize: ' +w + ',' + h );
27496         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27497         var ew = false;
27498         var eh = false;
27499         
27500         if(this.inputEl() ){
27501             if(typeof w == 'number'){
27502                 var aw = w - this.wrap.getFrameWidth('lr');
27503                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27504                 ew = aw;
27505             }
27506             if(typeof h == 'number'){
27507                  var tbh = -11;  // fixme it needs to tool bar size!
27508                 for (var i =0; i < this.toolbars.length;i++) {
27509                     // fixme - ask toolbars for heights?
27510                     tbh += this.toolbars[i].el.getHeight();
27511                     //if (this.toolbars[i].footer) {
27512                     //    tbh += this.toolbars[i].footer.el.getHeight();
27513                     //}
27514                 }
27515               
27516                 
27517                 
27518                 
27519                 
27520                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27521                 ah -= 5; // knock a few pixes off for look..
27522                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27523                 var eh = ah;
27524             }
27525         }
27526         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27527         this.editorcore.onResize(ew,eh);
27528         
27529     },
27530
27531     /**
27532      * Toggles the editor between standard and source edit mode.
27533      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27534      */
27535     toggleSourceEdit : function(sourceEditMode)
27536     {
27537         this.editorcore.toggleSourceEdit(sourceEditMode);
27538         
27539         if(this.editorcore.sourceEditMode){
27540             Roo.log('editor - showing textarea');
27541             
27542 //            Roo.log('in');
27543 //            Roo.log(this.syncValue());
27544             this.syncValue();
27545             this.inputEl().removeClass(['hide', 'x-hidden']);
27546             this.inputEl().dom.removeAttribute('tabIndex');
27547             this.inputEl().focus();
27548         }else{
27549             Roo.log('editor - hiding textarea');
27550 //            Roo.log('out')
27551 //            Roo.log(this.pushValue()); 
27552             this.pushValue();
27553             
27554             this.inputEl().addClass(['hide', 'x-hidden']);
27555             this.inputEl().dom.setAttribute('tabIndex', -1);
27556             //this.deferFocus();
27557         }
27558          
27559         if(this.resizable){
27560             this.setSize(this.wrap.getSize());
27561         }
27562         
27563         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27564     },
27565  
27566     // private (for BoxComponent)
27567     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27568
27569     // private (for BoxComponent)
27570     getResizeEl : function(){
27571         return this.wrap;
27572     },
27573
27574     // private (for BoxComponent)
27575     getPositionEl : function(){
27576         return this.wrap;
27577     },
27578
27579     // private
27580     initEvents : function(){
27581         this.originalValue = this.getValue();
27582     },
27583
27584 //    /**
27585 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27586 //     * @method
27587 //     */
27588 //    markInvalid : Roo.emptyFn,
27589 //    /**
27590 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27591 //     * @method
27592 //     */
27593 //    clearInvalid : Roo.emptyFn,
27594
27595     setValue : function(v){
27596         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27597         this.editorcore.pushValue();
27598     },
27599
27600      
27601     // private
27602     deferFocus : function(){
27603         this.focus.defer(10, this);
27604     },
27605
27606     // doc'ed in Field
27607     focus : function(){
27608         this.editorcore.focus();
27609         
27610     },
27611       
27612
27613     // private
27614     onDestroy : function(){
27615         
27616         
27617         
27618         if(this.rendered){
27619             
27620             for (var i =0; i < this.toolbars.length;i++) {
27621                 // fixme - ask toolbars for heights?
27622                 this.toolbars[i].onDestroy();
27623             }
27624             
27625             this.wrap.dom.innerHTML = '';
27626             this.wrap.remove();
27627         }
27628     },
27629
27630     // private
27631     onFirstFocus : function(){
27632         //Roo.log("onFirstFocus");
27633         this.editorcore.onFirstFocus();
27634          for (var i =0; i < this.toolbars.length;i++) {
27635             this.toolbars[i].onFirstFocus();
27636         }
27637         
27638     },
27639     
27640     // private
27641     syncValue : function()
27642     {   
27643         this.editorcore.syncValue();
27644     },
27645     
27646     pushValue : function()
27647     {   
27648         this.editorcore.pushValue();
27649     }
27650      
27651     
27652     // hide stuff that is not compatible
27653     /**
27654      * @event blur
27655      * @hide
27656      */
27657     /**
27658      * @event change
27659      * @hide
27660      */
27661     /**
27662      * @event focus
27663      * @hide
27664      */
27665     /**
27666      * @event specialkey
27667      * @hide
27668      */
27669     /**
27670      * @cfg {String} fieldClass @hide
27671      */
27672     /**
27673      * @cfg {String} focusClass @hide
27674      */
27675     /**
27676      * @cfg {String} autoCreate @hide
27677      */
27678     /**
27679      * @cfg {String} inputType @hide
27680      */
27681      
27682     /**
27683      * @cfg {String} invalidText @hide
27684      */
27685     /**
27686      * @cfg {String} msgFx @hide
27687      */
27688     /**
27689      * @cfg {String} validateOnBlur @hide
27690      */
27691 });
27692  
27693     
27694    
27695    
27696    
27697       
27698 Roo.namespace('Roo.bootstrap.htmleditor');
27699 /**
27700  * @class Roo.bootstrap.HtmlEditorToolbar1
27701  * Basic Toolbar
27702  * 
27703  * @example
27704  * Usage:
27705  *
27706  new Roo.bootstrap.HtmlEditor({
27707     ....
27708     toolbars : [
27709         new Roo.bootstrap.HtmlEditorToolbar1({
27710             disable : { fonts: 1 , format: 1, ..., ... , ...],
27711             btns : [ .... ]
27712         })
27713     }
27714      
27715  * 
27716  * @cfg {Object} disable List of elements to disable..
27717  * @cfg {Array} btns List of additional buttons.
27718  * 
27719  * 
27720  * NEEDS Extra CSS? 
27721  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27722  */
27723  
27724 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27725 {
27726     
27727     Roo.apply(this, config);
27728     
27729     // default disabled, based on 'good practice'..
27730     this.disable = this.disable || {};
27731     Roo.applyIf(this.disable, {
27732         fontSize : true,
27733         colors : true,
27734         specialElements : true
27735     });
27736     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27737     
27738     this.editor = config.editor;
27739     this.editorcore = config.editor.editorcore;
27740     
27741     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27742     
27743     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27744     // dont call parent... till later.
27745 }
27746 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27747      
27748     bar : true,
27749     
27750     editor : false,
27751     editorcore : false,
27752     
27753     
27754     formats : [
27755         "p" ,  
27756         "h1","h2","h3","h4","h5","h6", 
27757         "pre", "code", 
27758         "abbr", "acronym", "address", "cite", "samp", "var",
27759         'div','span'
27760     ],
27761     
27762     onRender : function(ct, position)
27763     {
27764        // Roo.log("Call onRender: " + this.xtype);
27765         
27766        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27767        Roo.log(this.el);
27768        this.el.dom.style.marginBottom = '0';
27769        var _this = this;
27770        var editorcore = this.editorcore;
27771        var editor= this.editor;
27772        
27773        var children = [];
27774        var btn = function(id,cmd , toggle, handler, html){
27775        
27776             var  event = toggle ? 'toggle' : 'click';
27777        
27778             var a = {
27779                 size : 'sm',
27780                 xtype: 'Button',
27781                 xns: Roo.bootstrap,
27782                 //glyphicon : id,
27783                 fa: id,
27784                 cmd : id || cmd,
27785                 enableToggle:toggle !== false,
27786                 html : html || '',
27787                 pressed : toggle ? false : null,
27788                 listeners : {}
27789             };
27790             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27791                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27792             };
27793             children.push(a);
27794             return a;
27795        }
27796        
27797     //    var cb_box = function...
27798         
27799         var style = {
27800                 xtype: 'Button',
27801                 size : 'sm',
27802                 xns: Roo.bootstrap,
27803                 fa : 'font',
27804                 //html : 'submit'
27805                 menu : {
27806                     xtype: 'Menu',
27807                     xns: Roo.bootstrap,
27808                     items:  []
27809                 }
27810         };
27811         Roo.each(this.formats, function(f) {
27812             style.menu.items.push({
27813                 xtype :'MenuItem',
27814                 xns: Roo.bootstrap,
27815                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27816                 tagname : f,
27817                 listeners : {
27818                     click : function()
27819                     {
27820                         editorcore.insertTag(this.tagname);
27821                         editor.focus();
27822                     }
27823                 }
27824                 
27825             });
27826         });
27827         children.push(style);   
27828         
27829         btn('bold',false,true);
27830         btn('italic',false,true);
27831         btn('align-left', 'justifyleft',true);
27832         btn('align-center', 'justifycenter',true);
27833         btn('align-right' , 'justifyright',true);
27834         btn('link', false, false, function(btn) {
27835             //Roo.log("create link?");
27836             var url = prompt(this.createLinkText, this.defaultLinkValue);
27837             if(url && url != 'http:/'+'/'){
27838                 this.editorcore.relayCmd('createlink', url);
27839             }
27840         }),
27841         btn('list','insertunorderedlist',true);
27842         btn('pencil', false,true, function(btn){
27843                 Roo.log(this);
27844                 this.toggleSourceEdit(btn.pressed);
27845         });
27846         
27847         if (this.editor.btns.length > 0) {
27848             for (var i = 0; i<this.editor.btns.length; i++) {
27849                 children.push(this.editor.btns[i]);
27850             }
27851         }
27852         
27853         /*
27854         var cog = {
27855                 xtype: 'Button',
27856                 size : 'sm',
27857                 xns: Roo.bootstrap,
27858                 glyphicon : 'cog',
27859                 //html : 'submit'
27860                 menu : {
27861                     xtype: 'Menu',
27862                     xns: Roo.bootstrap,
27863                     items:  []
27864                 }
27865         };
27866         
27867         cog.menu.items.push({
27868             xtype :'MenuItem',
27869             xns: Roo.bootstrap,
27870             html : Clean styles,
27871             tagname : f,
27872             listeners : {
27873                 click : function()
27874                 {
27875                     editorcore.insertTag(this.tagname);
27876                     editor.focus();
27877                 }
27878             }
27879             
27880         });
27881        */
27882         
27883          
27884        this.xtype = 'NavSimplebar';
27885         
27886         for(var i=0;i< children.length;i++) {
27887             
27888             this.buttons.add(this.addxtypeChild(children[i]));
27889             
27890         }
27891         
27892         editor.on('editorevent', this.updateToolbar, this);
27893     },
27894     onBtnClick : function(id)
27895     {
27896        this.editorcore.relayCmd(id);
27897        this.editorcore.focus();
27898     },
27899     
27900     /**
27901      * Protected method that will not generally be called directly. It triggers
27902      * a toolbar update by reading the markup state of the current selection in the editor.
27903      */
27904     updateToolbar: function(){
27905
27906         if(!this.editorcore.activated){
27907             this.editor.onFirstFocus(); // is this neeed?
27908             return;
27909         }
27910
27911         var btns = this.buttons; 
27912         var doc = this.editorcore.doc;
27913         btns.get('bold').setActive(doc.queryCommandState('bold'));
27914         btns.get('italic').setActive(doc.queryCommandState('italic'));
27915         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27916         
27917         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27918         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27919         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27920         
27921         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27922         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27923          /*
27924         
27925         var ans = this.editorcore.getAllAncestors();
27926         if (this.formatCombo) {
27927             
27928             
27929             var store = this.formatCombo.store;
27930             this.formatCombo.setValue("");
27931             for (var i =0; i < ans.length;i++) {
27932                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27933                     // select it..
27934                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27935                     break;
27936                 }
27937             }
27938         }
27939         
27940         
27941         
27942         // hides menus... - so this cant be on a menu...
27943         Roo.bootstrap.MenuMgr.hideAll();
27944         */
27945         Roo.bootstrap.MenuMgr.hideAll();
27946         //this.editorsyncValue();
27947     },
27948     onFirstFocus: function() {
27949         this.buttons.each(function(item){
27950            item.enable();
27951         });
27952     },
27953     toggleSourceEdit : function(sourceEditMode){
27954         
27955           
27956         if(sourceEditMode){
27957             Roo.log("disabling buttons");
27958            this.buttons.each( function(item){
27959                 if(item.cmd != 'pencil'){
27960                     item.disable();
27961                 }
27962             });
27963           
27964         }else{
27965             Roo.log("enabling buttons");
27966             if(this.editorcore.initialized){
27967                 this.buttons.each( function(item){
27968                     item.enable();
27969                 });
27970             }
27971             
27972         }
27973         Roo.log("calling toggole on editor");
27974         // tell the editor that it's been pressed..
27975         this.editor.toggleSourceEdit(sourceEditMode);
27976        
27977     }
27978 });
27979
27980
27981
27982
27983  
27984 /*
27985  * - LGPL
27986  */
27987
27988 /**
27989  * @class Roo.bootstrap.Markdown
27990  * @extends Roo.bootstrap.TextArea
27991  * Bootstrap Showdown editable area
27992  * @cfg {string} content
27993  * 
27994  * @constructor
27995  * Create a new Showdown
27996  */
27997
27998 Roo.bootstrap.Markdown = function(config){
27999     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28000    
28001 };
28002
28003 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28004     
28005     editing :false,
28006     
28007     initEvents : function()
28008     {
28009         
28010         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28011         this.markdownEl = this.el.createChild({
28012             cls : 'roo-markdown-area'
28013         });
28014         this.inputEl().addClass('d-none');
28015         if (this.getValue() == '') {
28016             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28017             
28018         } else {
28019             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28020         }
28021         this.markdownEl.on('click', this.toggleTextEdit, this);
28022         this.on('blur', this.toggleTextEdit, this);
28023         this.on('specialkey', this.resizeTextArea, this);
28024     },
28025     
28026     toggleTextEdit : function()
28027     {
28028         var sh = this.markdownEl.getHeight();
28029         this.inputEl().addClass('d-none');
28030         this.markdownEl.addClass('d-none');
28031         if (!this.editing) {
28032             // show editor?
28033             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28034             this.inputEl().removeClass('d-none');
28035             this.inputEl().focus();
28036             this.editing = true;
28037             return;
28038         }
28039         // show showdown...
28040         this.updateMarkdown();
28041         this.markdownEl.removeClass('d-none');
28042         this.editing = false;
28043         return;
28044     },
28045     updateMarkdown : function()
28046     {
28047         if (this.getValue() == '') {
28048             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28049             return;
28050         }
28051  
28052         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28053     },
28054     
28055     resizeTextArea: function () {
28056         
28057         var sh = 100;
28058         Roo.log([sh, this.getValue().split("\n").length * 30]);
28059         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28060     },
28061     setValue : function(val)
28062     {
28063         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28064         if (!this.editing) {
28065             this.updateMarkdown();
28066         }
28067         
28068     },
28069     focus : function()
28070     {
28071         if (!this.editing) {
28072             this.toggleTextEdit();
28073         }
28074         
28075     }
28076
28077
28078 });/*
28079  * Based on:
28080  * Ext JS Library 1.1.1
28081  * Copyright(c) 2006-2007, Ext JS, LLC.
28082  *
28083  * Originally Released Under LGPL - original licence link has changed is not relivant.
28084  *
28085  * Fork - LGPL
28086  * <script type="text/javascript">
28087  */
28088  
28089 /**
28090  * @class Roo.bootstrap.PagingToolbar
28091  * @extends Roo.bootstrap.NavSimplebar
28092  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28093  * @constructor
28094  * Create a new PagingToolbar
28095  * @param {Object} config The config object
28096  * @param {Roo.data.Store} store
28097  */
28098 Roo.bootstrap.PagingToolbar = function(config)
28099 {
28100     // old args format still supported... - xtype is prefered..
28101         // created from xtype...
28102     
28103     this.ds = config.dataSource;
28104     
28105     if (config.store && !this.ds) {
28106         this.store= Roo.factory(config.store, Roo.data);
28107         this.ds = this.store;
28108         this.ds.xmodule = this.xmodule || false;
28109     }
28110     
28111     this.toolbarItems = [];
28112     if (config.items) {
28113         this.toolbarItems = config.items;
28114     }
28115     
28116     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28117     
28118     this.cursor = 0;
28119     
28120     if (this.ds) { 
28121         this.bind(this.ds);
28122     }
28123     
28124     if (Roo.bootstrap.version == 4) {
28125         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28126     } else {
28127         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28128     }
28129     
28130 };
28131
28132 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28133     /**
28134      * @cfg {Roo.data.Store} dataSource
28135      * The underlying data store providing the paged data
28136      */
28137     /**
28138      * @cfg {String/HTMLElement/Element} container
28139      * container The id or element that will contain the toolbar
28140      */
28141     /**
28142      * @cfg {Boolean} displayInfo
28143      * True to display the displayMsg (defaults to false)
28144      */
28145     /**
28146      * @cfg {Number} pageSize
28147      * The number of records to display per page (defaults to 20)
28148      */
28149     pageSize: 20,
28150     /**
28151      * @cfg {String} displayMsg
28152      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28153      */
28154     displayMsg : 'Displaying {0} - {1} of {2}',
28155     /**
28156      * @cfg {String} emptyMsg
28157      * The message to display when no records are found (defaults to "No data to display")
28158      */
28159     emptyMsg : 'No data to display',
28160     /**
28161      * Customizable piece of the default paging text (defaults to "Page")
28162      * @type String
28163      */
28164     beforePageText : "Page",
28165     /**
28166      * Customizable piece of the default paging text (defaults to "of %0")
28167      * @type String
28168      */
28169     afterPageText : "of {0}",
28170     /**
28171      * Customizable piece of the default paging text (defaults to "First Page")
28172      * @type String
28173      */
28174     firstText : "First Page",
28175     /**
28176      * Customizable piece of the default paging text (defaults to "Previous Page")
28177      * @type String
28178      */
28179     prevText : "Previous Page",
28180     /**
28181      * Customizable piece of the default paging text (defaults to "Next Page")
28182      * @type String
28183      */
28184     nextText : "Next Page",
28185     /**
28186      * Customizable piece of the default paging text (defaults to "Last Page")
28187      * @type String
28188      */
28189     lastText : "Last Page",
28190     /**
28191      * Customizable piece of the default paging text (defaults to "Refresh")
28192      * @type String
28193      */
28194     refreshText : "Refresh",
28195
28196     buttons : false,
28197     // private
28198     onRender : function(ct, position) 
28199     {
28200         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28201         this.navgroup.parentId = this.id;
28202         this.navgroup.onRender(this.el, null);
28203         // add the buttons to the navgroup
28204         
28205         if(this.displayInfo){
28206             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28207             this.displayEl = this.el.select('.x-paging-info', true).first();
28208 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28209 //            this.displayEl = navel.el.select('span',true).first();
28210         }
28211         
28212         var _this = this;
28213         
28214         if(this.buttons){
28215             Roo.each(_this.buttons, function(e){ // this might need to use render????
28216                Roo.factory(e).render(_this.el);
28217             });
28218         }
28219             
28220         Roo.each(_this.toolbarItems, function(e) {
28221             _this.navgroup.addItem(e);
28222         });
28223         
28224         
28225         this.first = this.navgroup.addItem({
28226             tooltip: this.firstText,
28227             cls: "prev btn-outline-secondary",
28228             html : ' <i class="fa fa-step-backward"></i>',
28229             disabled: true,
28230             preventDefault: true,
28231             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28232         });
28233         
28234         this.prev =  this.navgroup.addItem({
28235             tooltip: this.prevText,
28236             cls: "prev btn-outline-secondary",
28237             html : ' <i class="fa fa-backward"></i>',
28238             disabled: true,
28239             preventDefault: true,
28240             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28241         });
28242     //this.addSeparator();
28243         
28244         
28245         var field = this.navgroup.addItem( {
28246             tagtype : 'span',
28247             cls : 'x-paging-position  btn-outline-secondary',
28248              disabled: true,
28249             html : this.beforePageText  +
28250                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28251                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28252          } ); //?? escaped?
28253         
28254         this.field = field.el.select('input', true).first();
28255         this.field.on("keydown", this.onPagingKeydown, this);
28256         this.field.on("focus", function(){this.dom.select();});
28257     
28258     
28259         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28260         //this.field.setHeight(18);
28261         //this.addSeparator();
28262         this.next = this.navgroup.addItem({
28263             tooltip: this.nextText,
28264             cls: "next btn-outline-secondary",
28265             html : ' <i class="fa fa-forward"></i>',
28266             disabled: true,
28267             preventDefault: true,
28268             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28269         });
28270         this.last = this.navgroup.addItem({
28271             tooltip: this.lastText,
28272             html : ' <i class="fa fa-step-forward"></i>',
28273             cls: "next btn-outline-secondary",
28274             disabled: true,
28275             preventDefault: true,
28276             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28277         });
28278     //this.addSeparator();
28279         this.loading = this.navgroup.addItem({
28280             tooltip: this.refreshText,
28281             cls: "btn-outline-secondary",
28282             html : ' <i class="fa fa-refresh"></i>',
28283             preventDefault: true,
28284             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28285         });
28286         
28287     },
28288
28289     // private
28290     updateInfo : function(){
28291         if(this.displayEl){
28292             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28293             var msg = count == 0 ?
28294                 this.emptyMsg :
28295                 String.format(
28296                     this.displayMsg,
28297                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28298                 );
28299             this.displayEl.update(msg);
28300         }
28301     },
28302
28303     // private
28304     onLoad : function(ds, r, o)
28305     {
28306         this.cursor = o.params && o.params.start ? o.params.start : 0;
28307         
28308         var d = this.getPageData(),
28309             ap = d.activePage,
28310             ps = d.pages;
28311         
28312         
28313         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28314         this.field.dom.value = ap;
28315         this.first.setDisabled(ap == 1);
28316         this.prev.setDisabled(ap == 1);
28317         this.next.setDisabled(ap == ps);
28318         this.last.setDisabled(ap == ps);
28319         this.loading.enable();
28320         this.updateInfo();
28321     },
28322
28323     // private
28324     getPageData : function(){
28325         var total = this.ds.getTotalCount();
28326         return {
28327             total : total,
28328             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28329             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28330         };
28331     },
28332
28333     // private
28334     onLoadError : function(){
28335         this.loading.enable();
28336     },
28337
28338     // private
28339     onPagingKeydown : function(e){
28340         var k = e.getKey();
28341         var d = this.getPageData();
28342         if(k == e.RETURN){
28343             var v = this.field.dom.value, pageNum;
28344             if(!v || isNaN(pageNum = parseInt(v, 10))){
28345                 this.field.dom.value = d.activePage;
28346                 return;
28347             }
28348             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28349             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28350             e.stopEvent();
28351         }
28352         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))
28353         {
28354           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28355           this.field.dom.value = pageNum;
28356           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28357           e.stopEvent();
28358         }
28359         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28360         {
28361           var v = this.field.dom.value, pageNum; 
28362           var increment = (e.shiftKey) ? 10 : 1;
28363           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28364                 increment *= -1;
28365           }
28366           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28367             this.field.dom.value = d.activePage;
28368             return;
28369           }
28370           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28371           {
28372             this.field.dom.value = parseInt(v, 10) + increment;
28373             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28374             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28375           }
28376           e.stopEvent();
28377         }
28378     },
28379
28380     // private
28381     beforeLoad : function(){
28382         if(this.loading){
28383             this.loading.disable();
28384         }
28385     },
28386
28387     // private
28388     onClick : function(which){
28389         
28390         var ds = this.ds;
28391         if (!ds) {
28392             return;
28393         }
28394         
28395         switch(which){
28396             case "first":
28397                 ds.load({params:{start: 0, limit: this.pageSize}});
28398             break;
28399             case "prev":
28400                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28401             break;
28402             case "next":
28403                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28404             break;
28405             case "last":
28406                 var total = ds.getTotalCount();
28407                 var extra = total % this.pageSize;
28408                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28409                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28410             break;
28411             case "refresh":
28412                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28413             break;
28414         }
28415     },
28416
28417     /**
28418      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28419      * @param {Roo.data.Store} store The data store to unbind
28420      */
28421     unbind : function(ds){
28422         ds.un("beforeload", this.beforeLoad, this);
28423         ds.un("load", this.onLoad, this);
28424         ds.un("loadexception", this.onLoadError, this);
28425         ds.un("remove", this.updateInfo, this);
28426         ds.un("add", this.updateInfo, this);
28427         this.ds = undefined;
28428     },
28429
28430     /**
28431      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28432      * @param {Roo.data.Store} store The data store to bind
28433      */
28434     bind : function(ds){
28435         ds.on("beforeload", this.beforeLoad, this);
28436         ds.on("load", this.onLoad, this);
28437         ds.on("loadexception", this.onLoadError, this);
28438         ds.on("remove", this.updateInfo, this);
28439         ds.on("add", this.updateInfo, this);
28440         this.ds = ds;
28441     }
28442 });/*
28443  * - LGPL
28444  *
28445  * element
28446  * 
28447  */
28448
28449 /**
28450  * @class Roo.bootstrap.MessageBar
28451  * @extends Roo.bootstrap.Component
28452  * Bootstrap MessageBar class
28453  * @cfg {String} html contents of the MessageBar
28454  * @cfg {String} weight (info | success | warning | danger) default info
28455  * @cfg {String} beforeClass insert the bar before the given class
28456  * @cfg {Boolean} closable (true | false) default false
28457  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28458  * 
28459  * @constructor
28460  * Create a new Element
28461  * @param {Object} config The config object
28462  */
28463
28464 Roo.bootstrap.MessageBar = function(config){
28465     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28466 };
28467
28468 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28469     
28470     html: '',
28471     weight: 'info',
28472     closable: false,
28473     fixed: false,
28474     beforeClass: 'bootstrap-sticky-wrap',
28475     
28476     getAutoCreate : function(){
28477         
28478         var cfg = {
28479             tag: 'div',
28480             cls: 'alert alert-dismissable alert-' + this.weight,
28481             cn: [
28482                 {
28483                     tag: 'span',
28484                     cls: 'message',
28485                     html: this.html || ''
28486                 }
28487             ]
28488         };
28489         
28490         if(this.fixed){
28491             cfg.cls += ' alert-messages-fixed';
28492         }
28493         
28494         if(this.closable){
28495             cfg.cn.push({
28496                 tag: 'button',
28497                 cls: 'close',
28498                 html: 'x'
28499             });
28500         }
28501         
28502         return cfg;
28503     },
28504     
28505     onRender : function(ct, position)
28506     {
28507         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28508         
28509         if(!this.el){
28510             var cfg = Roo.apply({},  this.getAutoCreate());
28511             cfg.id = Roo.id();
28512             
28513             if (this.cls) {
28514                 cfg.cls += ' ' + this.cls;
28515             }
28516             if (this.style) {
28517                 cfg.style = this.style;
28518             }
28519             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28520             
28521             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28522         }
28523         
28524         this.el.select('>button.close').on('click', this.hide, this);
28525         
28526     },
28527     
28528     show : function()
28529     {
28530         if (!this.rendered) {
28531             this.render();
28532         }
28533         
28534         this.el.show();
28535         
28536         this.fireEvent('show', this);
28537         
28538     },
28539     
28540     hide : function()
28541     {
28542         if (!this.rendered) {
28543             this.render();
28544         }
28545         
28546         this.el.hide();
28547         
28548         this.fireEvent('hide', this);
28549     },
28550     
28551     update : function()
28552     {
28553 //        var e = this.el.dom.firstChild;
28554 //        
28555 //        if(this.closable){
28556 //            e = e.nextSibling;
28557 //        }
28558 //        
28559 //        e.data = this.html || '';
28560
28561         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28562     }
28563    
28564 });
28565
28566  
28567
28568      /*
28569  * - LGPL
28570  *
28571  * Graph
28572  * 
28573  */
28574
28575
28576 /**
28577  * @class Roo.bootstrap.Graph
28578  * @extends Roo.bootstrap.Component
28579  * Bootstrap Graph class
28580 > Prameters
28581  -sm {number} sm 4
28582  -md {number} md 5
28583  @cfg {String} graphtype  bar | vbar | pie
28584  @cfg {number} g_x coodinator | centre x (pie)
28585  @cfg {number} g_y coodinator | centre y (pie)
28586  @cfg {number} g_r radius (pie)
28587  @cfg {number} g_height height of the chart (respected by all elements in the set)
28588  @cfg {number} g_width width of the chart (respected by all elements in the set)
28589  @cfg {Object} title The title of the chart
28590     
28591  -{Array}  values
28592  -opts (object) options for the chart 
28593      o {
28594      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28595      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28596      o vgutter (number)
28597      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.
28598      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28599      o to
28600      o stretch (boolean)
28601      o }
28602  -opts (object) options for the pie
28603      o{
28604      o cut
28605      o startAngle (number)
28606      o endAngle (number)
28607      } 
28608  *
28609  * @constructor
28610  * Create a new Input
28611  * @param {Object} config The config object
28612  */
28613
28614 Roo.bootstrap.Graph = function(config){
28615     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28616     
28617     this.addEvents({
28618         // img events
28619         /**
28620          * @event click
28621          * The img click event for the img.
28622          * @param {Roo.EventObject} e
28623          */
28624         "click" : true
28625     });
28626 };
28627
28628 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28629     
28630     sm: 4,
28631     md: 5,
28632     graphtype: 'bar',
28633     g_height: 250,
28634     g_width: 400,
28635     g_x: 50,
28636     g_y: 50,
28637     g_r: 30,
28638     opts:{
28639         //g_colors: this.colors,
28640         g_type: 'soft',
28641         g_gutter: '20%'
28642
28643     },
28644     title : false,
28645
28646     getAutoCreate : function(){
28647         
28648         var cfg = {
28649             tag: 'div',
28650             html : null
28651         };
28652         
28653         
28654         return  cfg;
28655     },
28656
28657     onRender : function(ct,position){
28658         
28659         
28660         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28661         
28662         if (typeof(Raphael) == 'undefined') {
28663             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28664             return;
28665         }
28666         
28667         this.raphael = Raphael(this.el.dom);
28668         
28669                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28670                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28671                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28672                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28673                 /*
28674                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28675                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28676                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28677                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28678                 
28679                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28680                 r.barchart(330, 10, 300, 220, data1);
28681                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28682                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28683                 */
28684                 
28685                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28686                 // r.barchart(30, 30, 560, 250,  xdata, {
28687                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28688                 //     axis : "0 0 1 1",
28689                 //     axisxlabels :  xdata
28690                 //     //yvalues : cols,
28691                    
28692                 // });
28693 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28694 //        
28695 //        this.load(null,xdata,{
28696 //                axis : "0 0 1 1",
28697 //                axisxlabels :  xdata
28698 //                });
28699
28700     },
28701
28702     load : function(graphtype,xdata,opts)
28703     {
28704         this.raphael.clear();
28705         if(!graphtype) {
28706             graphtype = this.graphtype;
28707         }
28708         if(!opts){
28709             opts = this.opts;
28710         }
28711         var r = this.raphael,
28712             fin = function () {
28713                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28714             },
28715             fout = function () {
28716                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28717             },
28718             pfin = function() {
28719                 this.sector.stop();
28720                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28721
28722                 if (this.label) {
28723                     this.label[0].stop();
28724                     this.label[0].attr({ r: 7.5 });
28725                     this.label[1].attr({ "font-weight": 800 });
28726                 }
28727             },
28728             pfout = function() {
28729                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28730
28731                 if (this.label) {
28732                     this.label[0].animate({ r: 5 }, 500, "bounce");
28733                     this.label[1].attr({ "font-weight": 400 });
28734                 }
28735             };
28736
28737         switch(graphtype){
28738             case 'bar':
28739                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28740                 break;
28741             case 'hbar':
28742                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28743                 break;
28744             case 'pie':
28745 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28746 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28747 //            
28748                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28749                 
28750                 break;
28751
28752         }
28753         
28754         if(this.title){
28755             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28756         }
28757         
28758     },
28759     
28760     setTitle: function(o)
28761     {
28762         this.title = o;
28763     },
28764     
28765     initEvents: function() {
28766         
28767         if(!this.href){
28768             this.el.on('click', this.onClick, this);
28769         }
28770     },
28771     
28772     onClick : function(e)
28773     {
28774         Roo.log('img onclick');
28775         this.fireEvent('click', this, e);
28776     }
28777    
28778 });
28779
28780  
28781 /*
28782  * - LGPL
28783  *
28784  * numberBox
28785  * 
28786  */
28787 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28788
28789 /**
28790  * @class Roo.bootstrap.dash.NumberBox
28791  * @extends Roo.bootstrap.Component
28792  * Bootstrap NumberBox class
28793  * @cfg {String} headline Box headline
28794  * @cfg {String} content Box content
28795  * @cfg {String} icon Box icon
28796  * @cfg {String} footer Footer text
28797  * @cfg {String} fhref Footer href
28798  * 
28799  * @constructor
28800  * Create a new NumberBox
28801  * @param {Object} config The config object
28802  */
28803
28804
28805 Roo.bootstrap.dash.NumberBox = function(config){
28806     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28807     
28808 };
28809
28810 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28811     
28812     headline : '',
28813     content : '',
28814     icon : '',
28815     footer : '',
28816     fhref : '',
28817     ficon : '',
28818     
28819     getAutoCreate : function(){
28820         
28821         var cfg = {
28822             tag : 'div',
28823             cls : 'small-box ',
28824             cn : [
28825                 {
28826                     tag : 'div',
28827                     cls : 'inner',
28828                     cn :[
28829                         {
28830                             tag : 'h3',
28831                             cls : 'roo-headline',
28832                             html : this.headline
28833                         },
28834                         {
28835                             tag : 'p',
28836                             cls : 'roo-content',
28837                             html : this.content
28838                         }
28839                     ]
28840                 }
28841             ]
28842         };
28843         
28844         if(this.icon){
28845             cfg.cn.push({
28846                 tag : 'div',
28847                 cls : 'icon',
28848                 cn :[
28849                     {
28850                         tag : 'i',
28851                         cls : 'ion ' + this.icon
28852                     }
28853                 ]
28854             });
28855         }
28856         
28857         if(this.footer){
28858             var footer = {
28859                 tag : 'a',
28860                 cls : 'small-box-footer',
28861                 href : this.fhref || '#',
28862                 html : this.footer
28863             };
28864             
28865             cfg.cn.push(footer);
28866             
28867         }
28868         
28869         return  cfg;
28870     },
28871
28872     onRender : function(ct,position){
28873         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28874
28875
28876        
28877                 
28878     },
28879
28880     setHeadline: function (value)
28881     {
28882         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28883     },
28884     
28885     setFooter: function (value, href)
28886     {
28887         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28888         
28889         if(href){
28890             this.el.select('a.small-box-footer',true).first().attr('href', href);
28891         }
28892         
28893     },
28894
28895     setContent: function (value)
28896     {
28897         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28898     },
28899
28900     initEvents: function() 
28901     {   
28902         
28903     }
28904     
28905 });
28906
28907  
28908 /*
28909  * - LGPL
28910  *
28911  * TabBox
28912  * 
28913  */
28914 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28915
28916 /**
28917  * @class Roo.bootstrap.dash.TabBox
28918  * @extends Roo.bootstrap.Component
28919  * Bootstrap TabBox class
28920  * @cfg {String} title Title of the TabBox
28921  * @cfg {String} icon Icon of the TabBox
28922  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28923  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28924  * 
28925  * @constructor
28926  * Create a new TabBox
28927  * @param {Object} config The config object
28928  */
28929
28930
28931 Roo.bootstrap.dash.TabBox = function(config){
28932     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28933     this.addEvents({
28934         // raw events
28935         /**
28936          * @event addpane
28937          * When a pane is added
28938          * @param {Roo.bootstrap.dash.TabPane} pane
28939          */
28940         "addpane" : true,
28941         /**
28942          * @event activatepane
28943          * When a pane is activated
28944          * @param {Roo.bootstrap.dash.TabPane} pane
28945          */
28946         "activatepane" : true
28947         
28948          
28949     });
28950     
28951     this.panes = [];
28952 };
28953
28954 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28955
28956     title : '',
28957     icon : false,
28958     showtabs : true,
28959     tabScrollable : false,
28960     
28961     getChildContainer : function()
28962     {
28963         return this.el.select('.tab-content', true).first();
28964     },
28965     
28966     getAutoCreate : function(){
28967         
28968         var header = {
28969             tag: 'li',
28970             cls: 'pull-left header',
28971             html: this.title,
28972             cn : []
28973         };
28974         
28975         if(this.icon){
28976             header.cn.push({
28977                 tag: 'i',
28978                 cls: 'fa ' + this.icon
28979             });
28980         }
28981         
28982         var h = {
28983             tag: 'ul',
28984             cls: 'nav nav-tabs pull-right',
28985             cn: [
28986                 header
28987             ]
28988         };
28989         
28990         if(this.tabScrollable){
28991             h = {
28992                 tag: 'div',
28993                 cls: 'tab-header',
28994                 cn: [
28995                     {
28996                         tag: 'ul',
28997                         cls: 'nav nav-tabs pull-right',
28998                         cn: [
28999                             header
29000                         ]
29001                     }
29002                 ]
29003             };
29004         }
29005         
29006         var cfg = {
29007             tag: 'div',
29008             cls: 'nav-tabs-custom',
29009             cn: [
29010                 h,
29011                 {
29012                     tag: 'div',
29013                     cls: 'tab-content no-padding',
29014                     cn: []
29015                 }
29016             ]
29017         };
29018
29019         return  cfg;
29020     },
29021     initEvents : function()
29022     {
29023         //Roo.log('add add pane handler');
29024         this.on('addpane', this.onAddPane, this);
29025     },
29026      /**
29027      * Updates the box title
29028      * @param {String} html to set the title to.
29029      */
29030     setTitle : function(value)
29031     {
29032         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29033     },
29034     onAddPane : function(pane)
29035     {
29036         this.panes.push(pane);
29037         //Roo.log('addpane');
29038         //Roo.log(pane);
29039         // tabs are rendere left to right..
29040         if(!this.showtabs){
29041             return;
29042         }
29043         
29044         var ctr = this.el.select('.nav-tabs', true).first();
29045          
29046          
29047         var existing = ctr.select('.nav-tab',true);
29048         var qty = existing.getCount();;
29049         
29050         
29051         var tab = ctr.createChild({
29052             tag : 'li',
29053             cls : 'nav-tab' + (qty ? '' : ' active'),
29054             cn : [
29055                 {
29056                     tag : 'a',
29057                     href:'#',
29058                     html : pane.title
29059                 }
29060             ]
29061         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29062         pane.tab = tab;
29063         
29064         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29065         if (!qty) {
29066             pane.el.addClass('active');
29067         }
29068         
29069                 
29070     },
29071     onTabClick : function(ev,un,ob,pane)
29072     {
29073         //Roo.log('tab - prev default');
29074         ev.preventDefault();
29075         
29076         
29077         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29078         pane.tab.addClass('active');
29079         //Roo.log(pane.title);
29080         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29081         // technically we should have a deactivate event.. but maybe add later.
29082         // and it should not de-activate the selected tab...
29083         this.fireEvent('activatepane', pane);
29084         pane.el.addClass('active');
29085         pane.fireEvent('activate');
29086         
29087         
29088     },
29089     
29090     getActivePane : function()
29091     {
29092         var r = false;
29093         Roo.each(this.panes, function(p) {
29094             if(p.el.hasClass('active')){
29095                 r = p;
29096                 return false;
29097             }
29098             
29099             return;
29100         });
29101         
29102         return r;
29103     }
29104     
29105     
29106 });
29107
29108  
29109 /*
29110  * - LGPL
29111  *
29112  * Tab pane
29113  * 
29114  */
29115 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29116 /**
29117  * @class Roo.bootstrap.TabPane
29118  * @extends Roo.bootstrap.Component
29119  * Bootstrap TabPane class
29120  * @cfg {Boolean} active (false | true) Default false
29121  * @cfg {String} title title of panel
29122
29123  * 
29124  * @constructor
29125  * Create a new TabPane
29126  * @param {Object} config The config object
29127  */
29128
29129 Roo.bootstrap.dash.TabPane = function(config){
29130     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29131     
29132     this.addEvents({
29133         // raw events
29134         /**
29135          * @event activate
29136          * When a pane is activated
29137          * @param {Roo.bootstrap.dash.TabPane} pane
29138          */
29139         "activate" : true
29140          
29141     });
29142 };
29143
29144 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29145     
29146     active : false,
29147     title : '',
29148     
29149     // the tabBox that this is attached to.
29150     tab : false,
29151      
29152     getAutoCreate : function() 
29153     {
29154         var cfg = {
29155             tag: 'div',
29156             cls: 'tab-pane'
29157         };
29158         
29159         if(this.active){
29160             cfg.cls += ' active';
29161         }
29162         
29163         return cfg;
29164     },
29165     initEvents  : function()
29166     {
29167         //Roo.log('trigger add pane handler');
29168         this.parent().fireEvent('addpane', this)
29169     },
29170     
29171      /**
29172      * Updates the tab title 
29173      * @param {String} html to set the title to.
29174      */
29175     setTitle: function(str)
29176     {
29177         if (!this.tab) {
29178             return;
29179         }
29180         this.title = str;
29181         this.tab.select('a', true).first().dom.innerHTML = str;
29182         
29183     }
29184     
29185     
29186     
29187 });
29188
29189  
29190
29191
29192  /*
29193  * - LGPL
29194  *
29195  * menu
29196  * 
29197  */
29198 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29199
29200 /**
29201  * @class Roo.bootstrap.menu.Menu
29202  * @extends Roo.bootstrap.Component
29203  * Bootstrap Menu class - container for Menu
29204  * @cfg {String} html Text of the menu
29205  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29206  * @cfg {String} icon Font awesome icon
29207  * @cfg {String} pos Menu align to (top | bottom) default bottom
29208  * 
29209  * 
29210  * @constructor
29211  * Create a new Menu
29212  * @param {Object} config The config object
29213  */
29214
29215
29216 Roo.bootstrap.menu.Menu = function(config){
29217     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29218     
29219     this.addEvents({
29220         /**
29221          * @event beforeshow
29222          * Fires before this menu is displayed
29223          * @param {Roo.bootstrap.menu.Menu} this
29224          */
29225         beforeshow : true,
29226         /**
29227          * @event beforehide
29228          * Fires before this menu is hidden
29229          * @param {Roo.bootstrap.menu.Menu} this
29230          */
29231         beforehide : true,
29232         /**
29233          * @event show
29234          * Fires after this menu is displayed
29235          * @param {Roo.bootstrap.menu.Menu} this
29236          */
29237         show : true,
29238         /**
29239          * @event hide
29240          * Fires after this menu is hidden
29241          * @param {Roo.bootstrap.menu.Menu} this
29242          */
29243         hide : true,
29244         /**
29245          * @event click
29246          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29247          * @param {Roo.bootstrap.menu.Menu} this
29248          * @param {Roo.EventObject} e
29249          */
29250         click : true
29251     });
29252     
29253 };
29254
29255 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29256     
29257     submenu : false,
29258     html : '',
29259     weight : 'default',
29260     icon : false,
29261     pos : 'bottom',
29262     
29263     
29264     getChildContainer : function() {
29265         if(this.isSubMenu){
29266             return this.el;
29267         }
29268         
29269         return this.el.select('ul.dropdown-menu', true).first();  
29270     },
29271     
29272     getAutoCreate : function()
29273     {
29274         var text = [
29275             {
29276                 tag : 'span',
29277                 cls : 'roo-menu-text',
29278                 html : this.html
29279             }
29280         ];
29281         
29282         if(this.icon){
29283             text.unshift({
29284                 tag : 'i',
29285                 cls : 'fa ' + this.icon
29286             })
29287         }
29288         
29289         
29290         var cfg = {
29291             tag : 'div',
29292             cls : 'btn-group',
29293             cn : [
29294                 {
29295                     tag : 'button',
29296                     cls : 'dropdown-button btn btn-' + this.weight,
29297                     cn : text
29298                 },
29299                 {
29300                     tag : 'button',
29301                     cls : 'dropdown-toggle btn btn-' + this.weight,
29302                     cn : [
29303                         {
29304                             tag : 'span',
29305                             cls : 'caret'
29306                         }
29307                     ]
29308                 },
29309                 {
29310                     tag : 'ul',
29311                     cls : 'dropdown-menu'
29312                 }
29313             ]
29314             
29315         };
29316         
29317         if(this.pos == 'top'){
29318             cfg.cls += ' dropup';
29319         }
29320         
29321         if(this.isSubMenu){
29322             cfg = {
29323                 tag : 'ul',
29324                 cls : 'dropdown-menu'
29325             }
29326         }
29327         
29328         return cfg;
29329     },
29330     
29331     onRender : function(ct, position)
29332     {
29333         this.isSubMenu = ct.hasClass('dropdown-submenu');
29334         
29335         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29336     },
29337     
29338     initEvents : function() 
29339     {
29340         if(this.isSubMenu){
29341             return;
29342         }
29343         
29344         this.hidden = true;
29345         
29346         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29347         this.triggerEl.on('click', this.onTriggerPress, this);
29348         
29349         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29350         this.buttonEl.on('click', this.onClick, this);
29351         
29352     },
29353     
29354     list : function()
29355     {
29356         if(this.isSubMenu){
29357             return this.el;
29358         }
29359         
29360         return this.el.select('ul.dropdown-menu', true).first();
29361     },
29362     
29363     onClick : function(e)
29364     {
29365         this.fireEvent("click", this, e);
29366     },
29367     
29368     onTriggerPress  : function(e)
29369     {   
29370         if (this.isVisible()) {
29371             this.hide();
29372         } else {
29373             this.show();
29374         }
29375     },
29376     
29377     isVisible : function(){
29378         return !this.hidden;
29379     },
29380     
29381     show : function()
29382     {
29383         this.fireEvent("beforeshow", this);
29384         
29385         this.hidden = false;
29386         this.el.addClass('open');
29387         
29388         Roo.get(document).on("mouseup", this.onMouseUp, this);
29389         
29390         this.fireEvent("show", this);
29391         
29392         
29393     },
29394     
29395     hide : function()
29396     {
29397         this.fireEvent("beforehide", this);
29398         
29399         this.hidden = true;
29400         this.el.removeClass('open');
29401         
29402         Roo.get(document).un("mouseup", this.onMouseUp);
29403         
29404         this.fireEvent("hide", this);
29405     },
29406     
29407     onMouseUp : function()
29408     {
29409         this.hide();
29410     }
29411     
29412 });
29413
29414  
29415  /*
29416  * - LGPL
29417  *
29418  * menu item
29419  * 
29420  */
29421 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29422
29423 /**
29424  * @class Roo.bootstrap.menu.Item
29425  * @extends Roo.bootstrap.Component
29426  * Bootstrap MenuItem class
29427  * @cfg {Boolean} submenu (true | false) default false
29428  * @cfg {String} html text of the item
29429  * @cfg {String} href the link
29430  * @cfg {Boolean} disable (true | false) default false
29431  * @cfg {Boolean} preventDefault (true | false) default true
29432  * @cfg {String} icon Font awesome icon
29433  * @cfg {String} pos Submenu align to (left | right) default right 
29434  * 
29435  * 
29436  * @constructor
29437  * Create a new Item
29438  * @param {Object} config The config object
29439  */
29440
29441
29442 Roo.bootstrap.menu.Item = function(config){
29443     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29444     this.addEvents({
29445         /**
29446          * @event mouseover
29447          * Fires when the mouse is hovering over this menu
29448          * @param {Roo.bootstrap.menu.Item} this
29449          * @param {Roo.EventObject} e
29450          */
29451         mouseover : true,
29452         /**
29453          * @event mouseout
29454          * Fires when the mouse exits this menu
29455          * @param {Roo.bootstrap.menu.Item} this
29456          * @param {Roo.EventObject} e
29457          */
29458         mouseout : true,
29459         // raw events
29460         /**
29461          * @event click
29462          * The raw click event for the entire grid.
29463          * @param {Roo.EventObject} e
29464          */
29465         click : true
29466     });
29467 };
29468
29469 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29470     
29471     submenu : false,
29472     href : '',
29473     html : '',
29474     preventDefault: true,
29475     disable : false,
29476     icon : false,
29477     pos : 'right',
29478     
29479     getAutoCreate : function()
29480     {
29481         var text = [
29482             {
29483                 tag : 'span',
29484                 cls : 'roo-menu-item-text',
29485                 html : this.html
29486             }
29487         ];
29488         
29489         if(this.icon){
29490             text.unshift({
29491                 tag : 'i',
29492                 cls : 'fa ' + this.icon
29493             })
29494         }
29495         
29496         var cfg = {
29497             tag : 'li',
29498             cn : [
29499                 {
29500                     tag : 'a',
29501                     href : this.href || '#',
29502                     cn : text
29503                 }
29504             ]
29505         };
29506         
29507         if(this.disable){
29508             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29509         }
29510         
29511         if(this.submenu){
29512             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29513             
29514             if(this.pos == 'left'){
29515                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29516             }
29517         }
29518         
29519         return cfg;
29520     },
29521     
29522     initEvents : function() 
29523     {
29524         this.el.on('mouseover', this.onMouseOver, this);
29525         this.el.on('mouseout', this.onMouseOut, this);
29526         
29527         this.el.select('a', true).first().on('click', this.onClick, this);
29528         
29529     },
29530     
29531     onClick : function(e)
29532     {
29533         if(this.preventDefault){
29534             e.preventDefault();
29535         }
29536         
29537         this.fireEvent("click", this, e);
29538     },
29539     
29540     onMouseOver : function(e)
29541     {
29542         if(this.submenu && this.pos == 'left'){
29543             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29544         }
29545         
29546         this.fireEvent("mouseover", this, e);
29547     },
29548     
29549     onMouseOut : function(e)
29550     {
29551         this.fireEvent("mouseout", this, e);
29552     }
29553 });
29554
29555  
29556
29557  /*
29558  * - LGPL
29559  *
29560  * menu separator
29561  * 
29562  */
29563 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29564
29565 /**
29566  * @class Roo.bootstrap.menu.Separator
29567  * @extends Roo.bootstrap.Component
29568  * Bootstrap Separator class
29569  * 
29570  * @constructor
29571  * Create a new Separator
29572  * @param {Object} config The config object
29573  */
29574
29575
29576 Roo.bootstrap.menu.Separator = function(config){
29577     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29578 };
29579
29580 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29581     
29582     getAutoCreate : function(){
29583         var cfg = {
29584             tag : 'li',
29585             cls: 'dropdown-divider divider'
29586         };
29587         
29588         return cfg;
29589     }
29590    
29591 });
29592
29593  
29594
29595  /*
29596  * - LGPL
29597  *
29598  * Tooltip
29599  * 
29600  */
29601
29602 /**
29603  * @class Roo.bootstrap.Tooltip
29604  * Bootstrap Tooltip class
29605  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29606  * to determine which dom element triggers the tooltip.
29607  * 
29608  * It needs to add support for additional attributes like tooltip-position
29609  * 
29610  * @constructor
29611  * Create a new Toolti
29612  * @param {Object} config The config object
29613  */
29614
29615 Roo.bootstrap.Tooltip = function(config){
29616     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29617     
29618     this.alignment = Roo.bootstrap.Tooltip.alignment;
29619     
29620     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29621         this.alignment = config.alignment;
29622     }
29623     
29624 };
29625
29626 Roo.apply(Roo.bootstrap.Tooltip, {
29627     /**
29628      * @function init initialize tooltip monitoring.
29629      * @static
29630      */
29631     currentEl : false,
29632     currentTip : false,
29633     currentRegion : false,
29634     
29635     //  init : delay?
29636     
29637     init : function()
29638     {
29639         Roo.get(document).on('mouseover', this.enter ,this);
29640         Roo.get(document).on('mouseout', this.leave, this);
29641          
29642         
29643         this.currentTip = new Roo.bootstrap.Tooltip();
29644     },
29645     
29646     enter : function(ev)
29647     {
29648         var dom = ev.getTarget();
29649         
29650         //Roo.log(['enter',dom]);
29651         var el = Roo.fly(dom);
29652         if (this.currentEl) {
29653             //Roo.log(dom);
29654             //Roo.log(this.currentEl);
29655             //Roo.log(this.currentEl.contains(dom));
29656             if (this.currentEl == el) {
29657                 return;
29658             }
29659             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29660                 return;
29661             }
29662
29663         }
29664         
29665         if (this.currentTip.el) {
29666             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29667         }    
29668         //Roo.log(ev);
29669         
29670         if(!el || el.dom == document){
29671             return;
29672         }
29673         
29674         var bindEl = el; 
29675         var pel = false;
29676         if (!el.attr('tooltip')) {
29677             pel = el.findParent("[tooltip]");
29678             if (pel) {
29679                 bindEl = Roo.get(pel);
29680             }
29681         }
29682         
29683        
29684         
29685         // you can not look for children, as if el is the body.. then everythign is the child..
29686         if (!pel && !el.attr('tooltip')) { //
29687             if (!el.select("[tooltip]").elements.length) {
29688                 return;
29689             }
29690             // is the mouse over this child...?
29691             bindEl = el.select("[tooltip]").first();
29692             var xy = ev.getXY();
29693             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29694                 //Roo.log("not in region.");
29695                 return;
29696             }
29697             //Roo.log("child element over..");
29698             
29699         }
29700         this.currentEl = el;
29701         this.currentTip.bind(bindEl);
29702         this.currentRegion = Roo.lib.Region.getRegion(dom);
29703         this.currentTip.enter();
29704         
29705     },
29706     leave : function(ev)
29707     {
29708         var dom = ev.getTarget();
29709         //Roo.log(['leave',dom]);
29710         if (!this.currentEl) {
29711             return;
29712         }
29713         
29714         
29715         if (dom != this.currentEl.dom) {
29716             return;
29717         }
29718         var xy = ev.getXY();
29719         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29720             return;
29721         }
29722         // only activate leave if mouse cursor is outside... bounding box..
29723         
29724         
29725         
29726         
29727         if (this.currentTip) {
29728             this.currentTip.leave();
29729         }
29730         //Roo.log('clear currentEl');
29731         this.currentEl = false;
29732         
29733         
29734     },
29735     alignment : {
29736         'left' : ['r-l', [-2,0], 'right'],
29737         'right' : ['l-r', [2,0], 'left'],
29738         'bottom' : ['t-b', [0,2], 'top'],
29739         'top' : [ 'b-t', [0,-2], 'bottom']
29740     }
29741     
29742 });
29743
29744
29745 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29746     
29747     
29748     bindEl : false,
29749     
29750     delay : null, // can be { show : 300 , hide: 500}
29751     
29752     timeout : null,
29753     
29754     hoverState : null, //???
29755     
29756     placement : 'bottom', 
29757     
29758     alignment : false,
29759     
29760     getAutoCreate : function(){
29761     
29762         var cfg = {
29763            cls : 'tooltip',   
29764            role : 'tooltip',
29765            cn : [
29766                 {
29767                     cls : 'tooltip-arrow arrow'
29768                 },
29769                 {
29770                     cls : 'tooltip-inner'
29771                 }
29772            ]
29773         };
29774         
29775         return cfg;
29776     },
29777     bind : function(el)
29778     {
29779         this.bindEl = el;
29780     },
29781     
29782     initEvents : function()
29783     {
29784         this.arrowEl = this.el.select('.arrow', true).first();
29785         this.innerEl = this.el.select('.tooltip-inner', true).first();
29786     },
29787     
29788     enter : function () {
29789        
29790         if (this.timeout != null) {
29791             clearTimeout(this.timeout);
29792         }
29793         
29794         this.hoverState = 'in';
29795          //Roo.log("enter - show");
29796         if (!this.delay || !this.delay.show) {
29797             this.show();
29798             return;
29799         }
29800         var _t = this;
29801         this.timeout = setTimeout(function () {
29802             if (_t.hoverState == 'in') {
29803                 _t.show();
29804             }
29805         }, this.delay.show);
29806     },
29807     leave : function()
29808     {
29809         clearTimeout(this.timeout);
29810     
29811         this.hoverState = 'out';
29812          if (!this.delay || !this.delay.hide) {
29813             this.hide();
29814             return;
29815         }
29816        
29817         var _t = this;
29818         this.timeout = setTimeout(function () {
29819             //Roo.log("leave - timeout");
29820             
29821             if (_t.hoverState == 'out') {
29822                 _t.hide();
29823                 Roo.bootstrap.Tooltip.currentEl = false;
29824             }
29825         }, delay);
29826     },
29827     
29828     show : function (msg)
29829     {
29830         if (!this.el) {
29831             this.render(document.body);
29832         }
29833         // set content.
29834         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29835         
29836         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29837         
29838         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29839         
29840         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29841                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29842         
29843         var placement = typeof this.placement == 'function' ?
29844             this.placement.call(this, this.el, on_el) :
29845             this.placement;
29846             
29847         var autoToken = /\s?auto?\s?/i;
29848         var autoPlace = autoToken.test(placement);
29849         if (autoPlace) {
29850             placement = placement.replace(autoToken, '') || 'top';
29851         }
29852         
29853         //this.el.detach()
29854         //this.el.setXY([0,0]);
29855         this.el.show();
29856         //this.el.dom.style.display='block';
29857         
29858         //this.el.appendTo(on_el);
29859         
29860         var p = this.getPosition();
29861         var box = this.el.getBox();
29862         
29863         if (autoPlace) {
29864             // fixme..
29865         }
29866         
29867         var align = this.alignment[placement];
29868         
29869         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29870         
29871         if(placement == 'top' || placement == 'bottom'){
29872             if(xy[0] < 0){
29873                 placement = 'right';
29874             }
29875             
29876             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29877                 placement = 'left';
29878             }
29879             
29880             var scroll = Roo.select('body', true).first().getScroll();
29881             
29882             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29883                 placement = 'top';
29884             }
29885             
29886             align = this.alignment[placement];
29887             
29888             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29889             
29890         }
29891         
29892         var elems = document.getElementsByTagName('div');
29893         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29894         for (var i = 0; i < elems.length; i++) {
29895           var zindex = Number.parseInt(
29896                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29897                 10
29898           );
29899           if (zindex > highest) {
29900             highest = zindex;
29901           }
29902         }
29903         
29904         
29905         
29906         this.el.dom.style.zIndex = highest;
29907         
29908         this.el.alignTo(this.bindEl, align[0],align[1]);
29909         //var arrow = this.el.select('.arrow',true).first();
29910         //arrow.set(align[2], 
29911         
29912         this.el.addClass(placement);
29913         this.el.addClass("bs-tooltip-"+ placement);
29914         
29915         this.el.addClass('in fade show');
29916         
29917         this.hoverState = null;
29918         
29919         if (this.el.hasClass('fade')) {
29920             // fade it?
29921         }
29922         
29923         
29924         
29925         
29926         
29927     },
29928     hide : function()
29929     {
29930          
29931         if (!this.el) {
29932             return;
29933         }
29934         //this.el.setXY([0,0]);
29935         this.el.removeClass(['show', 'in']);
29936         //this.el.hide();
29937         
29938     }
29939     
29940 });
29941  
29942
29943  /*
29944  * - LGPL
29945  *
29946  * Location Picker
29947  * 
29948  */
29949
29950 /**
29951  * @class Roo.bootstrap.LocationPicker
29952  * @extends Roo.bootstrap.Component
29953  * Bootstrap LocationPicker class
29954  * @cfg {Number} latitude Position when init default 0
29955  * @cfg {Number} longitude Position when init default 0
29956  * @cfg {Number} zoom default 15
29957  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29958  * @cfg {Boolean} mapTypeControl default false
29959  * @cfg {Boolean} disableDoubleClickZoom default false
29960  * @cfg {Boolean} scrollwheel default true
29961  * @cfg {Boolean} streetViewControl default false
29962  * @cfg {Number} radius default 0
29963  * @cfg {String} locationName
29964  * @cfg {Boolean} draggable default true
29965  * @cfg {Boolean} enableAutocomplete default false
29966  * @cfg {Boolean} enableReverseGeocode default true
29967  * @cfg {String} markerTitle
29968  * 
29969  * @constructor
29970  * Create a new LocationPicker
29971  * @param {Object} config The config object
29972  */
29973
29974
29975 Roo.bootstrap.LocationPicker = function(config){
29976     
29977     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29978     
29979     this.addEvents({
29980         /**
29981          * @event initial
29982          * Fires when the picker initialized.
29983          * @param {Roo.bootstrap.LocationPicker} this
29984          * @param {Google Location} location
29985          */
29986         initial : true,
29987         /**
29988          * @event positionchanged
29989          * Fires when the picker position changed.
29990          * @param {Roo.bootstrap.LocationPicker} this
29991          * @param {Google Location} location
29992          */
29993         positionchanged : true,
29994         /**
29995          * @event resize
29996          * Fires when the map resize.
29997          * @param {Roo.bootstrap.LocationPicker} this
29998          */
29999         resize : true,
30000         /**
30001          * @event show
30002          * Fires when the map show.
30003          * @param {Roo.bootstrap.LocationPicker} this
30004          */
30005         show : true,
30006         /**
30007          * @event hide
30008          * Fires when the map hide.
30009          * @param {Roo.bootstrap.LocationPicker} this
30010          */
30011         hide : true,
30012         /**
30013          * @event mapClick
30014          * Fires when click the map.
30015          * @param {Roo.bootstrap.LocationPicker} this
30016          * @param {Map event} e
30017          */
30018         mapClick : true,
30019         /**
30020          * @event mapRightClick
30021          * Fires when right click the map.
30022          * @param {Roo.bootstrap.LocationPicker} this
30023          * @param {Map event} e
30024          */
30025         mapRightClick : true,
30026         /**
30027          * @event markerClick
30028          * Fires when click the marker.
30029          * @param {Roo.bootstrap.LocationPicker} this
30030          * @param {Map event} e
30031          */
30032         markerClick : true,
30033         /**
30034          * @event markerRightClick
30035          * Fires when right click the marker.
30036          * @param {Roo.bootstrap.LocationPicker} this
30037          * @param {Map event} e
30038          */
30039         markerRightClick : true,
30040         /**
30041          * @event OverlayViewDraw
30042          * Fires when OverlayView Draw
30043          * @param {Roo.bootstrap.LocationPicker} this
30044          */
30045         OverlayViewDraw : true,
30046         /**
30047          * @event OverlayViewOnAdd
30048          * Fires when OverlayView Draw
30049          * @param {Roo.bootstrap.LocationPicker} this
30050          */
30051         OverlayViewOnAdd : true,
30052         /**
30053          * @event OverlayViewOnRemove
30054          * Fires when OverlayView Draw
30055          * @param {Roo.bootstrap.LocationPicker} this
30056          */
30057         OverlayViewOnRemove : true,
30058         /**
30059          * @event OverlayViewShow
30060          * Fires when OverlayView Draw
30061          * @param {Roo.bootstrap.LocationPicker} this
30062          * @param {Pixel} cpx
30063          */
30064         OverlayViewShow : true,
30065         /**
30066          * @event OverlayViewHide
30067          * Fires when OverlayView Draw
30068          * @param {Roo.bootstrap.LocationPicker} this
30069          */
30070         OverlayViewHide : true,
30071         /**
30072          * @event loadexception
30073          * Fires when load google lib failed.
30074          * @param {Roo.bootstrap.LocationPicker} this
30075          */
30076         loadexception : true
30077     });
30078         
30079 };
30080
30081 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30082     
30083     gMapContext: false,
30084     
30085     latitude: 0,
30086     longitude: 0,
30087     zoom: 15,
30088     mapTypeId: false,
30089     mapTypeControl: false,
30090     disableDoubleClickZoom: false,
30091     scrollwheel: true,
30092     streetViewControl: false,
30093     radius: 0,
30094     locationName: '',
30095     draggable: true,
30096     enableAutocomplete: false,
30097     enableReverseGeocode: true,
30098     markerTitle: '',
30099     
30100     getAutoCreate: function()
30101     {
30102
30103         var cfg = {
30104             tag: 'div',
30105             cls: 'roo-location-picker'
30106         };
30107         
30108         return cfg
30109     },
30110     
30111     initEvents: function(ct, position)
30112     {       
30113         if(!this.el.getWidth() || this.isApplied()){
30114             return;
30115         }
30116         
30117         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30118         
30119         this.initial();
30120     },
30121     
30122     initial: function()
30123     {
30124         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30125             this.fireEvent('loadexception', this);
30126             return;
30127         }
30128         
30129         if(!this.mapTypeId){
30130             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30131         }
30132         
30133         this.gMapContext = this.GMapContext();
30134         
30135         this.initOverlayView();
30136         
30137         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30138         
30139         var _this = this;
30140                 
30141         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30142             _this.setPosition(_this.gMapContext.marker.position);
30143         });
30144         
30145         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30146             _this.fireEvent('mapClick', this, event);
30147             
30148         });
30149
30150         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30151             _this.fireEvent('mapRightClick', this, event);
30152             
30153         });
30154         
30155         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30156             _this.fireEvent('markerClick', this, event);
30157             
30158         });
30159
30160         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30161             _this.fireEvent('markerRightClick', this, event);
30162             
30163         });
30164         
30165         this.setPosition(this.gMapContext.location);
30166         
30167         this.fireEvent('initial', this, this.gMapContext.location);
30168     },
30169     
30170     initOverlayView: function()
30171     {
30172         var _this = this;
30173         
30174         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30175             
30176             draw: function()
30177             {
30178                 _this.fireEvent('OverlayViewDraw', _this);
30179             },
30180             
30181             onAdd: function()
30182             {
30183                 _this.fireEvent('OverlayViewOnAdd', _this);
30184             },
30185             
30186             onRemove: function()
30187             {
30188                 _this.fireEvent('OverlayViewOnRemove', _this);
30189             },
30190             
30191             show: function(cpx)
30192             {
30193                 _this.fireEvent('OverlayViewShow', _this, cpx);
30194             },
30195             
30196             hide: function()
30197             {
30198                 _this.fireEvent('OverlayViewHide', _this);
30199             }
30200             
30201         });
30202     },
30203     
30204     fromLatLngToContainerPixel: function(event)
30205     {
30206         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30207     },
30208     
30209     isApplied: function() 
30210     {
30211         return this.getGmapContext() == false ? false : true;
30212     },
30213     
30214     getGmapContext: function() 
30215     {
30216         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30217     },
30218     
30219     GMapContext: function() 
30220     {
30221         var position = new google.maps.LatLng(this.latitude, this.longitude);
30222         
30223         var _map = new google.maps.Map(this.el.dom, {
30224             center: position,
30225             zoom: this.zoom,
30226             mapTypeId: this.mapTypeId,
30227             mapTypeControl: this.mapTypeControl,
30228             disableDoubleClickZoom: this.disableDoubleClickZoom,
30229             scrollwheel: this.scrollwheel,
30230             streetViewControl: this.streetViewControl,
30231             locationName: this.locationName,
30232             draggable: this.draggable,
30233             enableAutocomplete: this.enableAutocomplete,
30234             enableReverseGeocode: this.enableReverseGeocode
30235         });
30236         
30237         var _marker = new google.maps.Marker({
30238             position: position,
30239             map: _map,
30240             title: this.markerTitle,
30241             draggable: this.draggable
30242         });
30243         
30244         return {
30245             map: _map,
30246             marker: _marker,
30247             circle: null,
30248             location: position,
30249             radius: this.radius,
30250             locationName: this.locationName,
30251             addressComponents: {
30252                 formatted_address: null,
30253                 addressLine1: null,
30254                 addressLine2: null,
30255                 streetName: null,
30256                 streetNumber: null,
30257                 city: null,
30258                 district: null,
30259                 state: null,
30260                 stateOrProvince: null
30261             },
30262             settings: this,
30263             domContainer: this.el.dom,
30264             geodecoder: new google.maps.Geocoder()
30265         };
30266     },
30267     
30268     drawCircle: function(center, radius, options) 
30269     {
30270         if (this.gMapContext.circle != null) {
30271             this.gMapContext.circle.setMap(null);
30272         }
30273         if (radius > 0) {
30274             radius *= 1;
30275             options = Roo.apply({}, options, {
30276                 strokeColor: "#0000FF",
30277                 strokeOpacity: .35,
30278                 strokeWeight: 2,
30279                 fillColor: "#0000FF",
30280                 fillOpacity: .2
30281             });
30282             
30283             options.map = this.gMapContext.map;
30284             options.radius = radius;
30285             options.center = center;
30286             this.gMapContext.circle = new google.maps.Circle(options);
30287             return this.gMapContext.circle;
30288         }
30289         
30290         return null;
30291     },
30292     
30293     setPosition: function(location) 
30294     {
30295         this.gMapContext.location = location;
30296         this.gMapContext.marker.setPosition(location);
30297         this.gMapContext.map.panTo(location);
30298         this.drawCircle(location, this.gMapContext.radius, {});
30299         
30300         var _this = this;
30301         
30302         if (this.gMapContext.settings.enableReverseGeocode) {
30303             this.gMapContext.geodecoder.geocode({
30304                 latLng: this.gMapContext.location
30305             }, function(results, status) {
30306                 
30307                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30308                     _this.gMapContext.locationName = results[0].formatted_address;
30309                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30310                     
30311                     _this.fireEvent('positionchanged', this, location);
30312                 }
30313             });
30314             
30315             return;
30316         }
30317         
30318         this.fireEvent('positionchanged', this, location);
30319     },
30320     
30321     resize: function()
30322     {
30323         google.maps.event.trigger(this.gMapContext.map, "resize");
30324         
30325         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30326         
30327         this.fireEvent('resize', this);
30328     },
30329     
30330     setPositionByLatLng: function(latitude, longitude)
30331     {
30332         this.setPosition(new google.maps.LatLng(latitude, longitude));
30333     },
30334     
30335     getCurrentPosition: function() 
30336     {
30337         return {
30338             latitude: this.gMapContext.location.lat(),
30339             longitude: this.gMapContext.location.lng()
30340         };
30341     },
30342     
30343     getAddressName: function() 
30344     {
30345         return this.gMapContext.locationName;
30346     },
30347     
30348     getAddressComponents: function() 
30349     {
30350         return this.gMapContext.addressComponents;
30351     },
30352     
30353     address_component_from_google_geocode: function(address_components) 
30354     {
30355         var result = {};
30356         
30357         for (var i = 0; i < address_components.length; i++) {
30358             var component = address_components[i];
30359             if (component.types.indexOf("postal_code") >= 0) {
30360                 result.postalCode = component.short_name;
30361             } else if (component.types.indexOf("street_number") >= 0) {
30362                 result.streetNumber = component.short_name;
30363             } else if (component.types.indexOf("route") >= 0) {
30364                 result.streetName = component.short_name;
30365             } else if (component.types.indexOf("neighborhood") >= 0) {
30366                 result.city = component.short_name;
30367             } else if (component.types.indexOf("locality") >= 0) {
30368                 result.city = component.short_name;
30369             } else if (component.types.indexOf("sublocality") >= 0) {
30370                 result.district = component.short_name;
30371             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30372                 result.stateOrProvince = component.short_name;
30373             } else if (component.types.indexOf("country") >= 0) {
30374                 result.country = component.short_name;
30375             }
30376         }
30377         
30378         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30379         result.addressLine2 = "";
30380         return result;
30381     },
30382     
30383     setZoomLevel: function(zoom)
30384     {
30385         this.gMapContext.map.setZoom(zoom);
30386     },
30387     
30388     show: function()
30389     {
30390         if(!this.el){
30391             return;
30392         }
30393         
30394         this.el.show();
30395         
30396         this.resize();
30397         
30398         this.fireEvent('show', this);
30399     },
30400     
30401     hide: function()
30402     {
30403         if(!this.el){
30404             return;
30405         }
30406         
30407         this.el.hide();
30408         
30409         this.fireEvent('hide', this);
30410     }
30411     
30412 });
30413
30414 Roo.apply(Roo.bootstrap.LocationPicker, {
30415     
30416     OverlayView : function(map, options)
30417     {
30418         options = options || {};
30419         
30420         this.setMap(map);
30421     }
30422     
30423     
30424 });/**
30425  * @class Roo.bootstrap.Alert
30426  * @extends Roo.bootstrap.Component
30427  * Bootstrap Alert class - shows an alert area box
30428  * eg
30429  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30430   Enter a valid email address
30431 </div>
30432  * @licence LGPL
30433  * @cfg {String} title The title of alert
30434  * @cfg {String} html The content of alert
30435  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30436  * @cfg {String} fa font-awesomeicon
30437  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30438  * @cfg {Boolean} close true to show a x closer
30439  * 
30440  * 
30441  * @constructor
30442  * Create a new alert
30443  * @param {Object} config The config object
30444  */
30445
30446
30447 Roo.bootstrap.Alert = function(config){
30448     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30449     
30450 };
30451
30452 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30453     
30454     title: '',
30455     html: '',
30456     weight: false,
30457     fa: false,
30458     faicon: false, // BC
30459     close : false,
30460     
30461     
30462     getAutoCreate : function()
30463     {
30464         
30465         var cfg = {
30466             tag : 'div',
30467             cls : 'alert',
30468             cn : [
30469                 {
30470                     tag: 'button',
30471                     type :  "button",
30472                     cls: "close",
30473                     html : '×',
30474                     style : this.close ? '' : 'display:none'
30475                 },
30476                 {
30477                     tag : 'i',
30478                     cls : 'roo-alert-icon'
30479                     
30480                 },
30481                 {
30482                     tag : 'b',
30483                     cls : 'roo-alert-title',
30484                     html : this.title
30485                 },
30486                 {
30487                     tag : 'span',
30488                     cls : 'roo-alert-text',
30489                     html : this.html
30490                 }
30491             ]
30492         };
30493         
30494         if(this.faicon){
30495             cfg.cn[0].cls += ' fa ' + this.faicon;
30496         }
30497         if(this.fa){
30498             cfg.cn[0].cls += ' fa ' + this.fa;
30499         }
30500         
30501         if(this.weight){
30502             cfg.cls += ' alert-' + this.weight;
30503         }
30504         
30505         return cfg;
30506     },
30507     
30508     initEvents: function() 
30509     {
30510         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30511         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30512         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30513         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30514         if (this.seconds > 0) {
30515             this.hide.defer(this.seconds, this);
30516         }
30517     },
30518     /**
30519      * Set the Title Message HTML
30520      * @param {String} html
30521      */
30522     setTitle : function(str)
30523     {
30524         this.titleEl.dom.innerHTML = str;
30525     },
30526      
30527      /**
30528      * Set the Body Message HTML
30529      * @param {String} html
30530      */
30531     setHtml : function(str)
30532     {
30533         this.htmlEl.dom.innerHTML = str;
30534     },
30535     /**
30536      * Set the Weight of the alert
30537      * @param {String} (success|info|warning|danger) weight
30538      */
30539     
30540     setWeight : function(weight)
30541     {
30542         if(this.weight){
30543             this.el.removeClass('alert-' + this.weight);
30544         }
30545         
30546         this.weight = weight;
30547         
30548         this.el.addClass('alert-' + this.weight);
30549     },
30550       /**
30551      * Set the Icon of the alert
30552      * @param {String} see fontawsome names (name without the 'fa-' bit)
30553      */
30554     setIcon : function(icon)
30555     {
30556         if(this.faicon){
30557             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30558         }
30559         
30560         this.faicon = icon;
30561         
30562         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30563     },
30564     /**
30565      * Hide the Alert
30566      */
30567     hide: function() 
30568     {
30569         this.el.hide();   
30570     },
30571     /**
30572      * Show the Alert
30573      */
30574     show: function() 
30575     {  
30576         this.el.show();   
30577     }
30578     
30579 });
30580
30581  
30582 /*
30583 * Licence: LGPL
30584 */
30585
30586 /**
30587  * @class Roo.bootstrap.UploadCropbox
30588  * @extends Roo.bootstrap.Component
30589  * Bootstrap UploadCropbox class
30590  * @cfg {String} emptyText show when image has been loaded
30591  * @cfg {String} rotateNotify show when image too small to rotate
30592  * @cfg {Number} errorTimeout default 3000
30593  * @cfg {Number} minWidth default 300
30594  * @cfg {Number} minHeight default 300
30595  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30596  * @cfg {Boolean} isDocument (true|false) default false
30597  * @cfg {String} url action url
30598  * @cfg {String} paramName default 'imageUpload'
30599  * @cfg {String} method default POST
30600  * @cfg {Boolean} loadMask (true|false) default true
30601  * @cfg {Boolean} loadingText default 'Loading...'
30602  * 
30603  * @constructor
30604  * Create a new UploadCropbox
30605  * @param {Object} config The config object
30606  */
30607
30608 Roo.bootstrap.UploadCropbox = function(config){
30609     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30610     
30611     this.addEvents({
30612         /**
30613          * @event beforeselectfile
30614          * Fire before select file
30615          * @param {Roo.bootstrap.UploadCropbox} this
30616          */
30617         "beforeselectfile" : true,
30618         /**
30619          * @event initial
30620          * Fire after initEvent
30621          * @param {Roo.bootstrap.UploadCropbox} this
30622          */
30623         "initial" : true,
30624         /**
30625          * @event crop
30626          * Fire after initEvent
30627          * @param {Roo.bootstrap.UploadCropbox} this
30628          * @param {String} data
30629          */
30630         "crop" : true,
30631         /**
30632          * @event prepare
30633          * Fire when preparing the file data
30634          * @param {Roo.bootstrap.UploadCropbox} this
30635          * @param {Object} file
30636          */
30637         "prepare" : true,
30638         /**
30639          * @event exception
30640          * Fire when get exception
30641          * @param {Roo.bootstrap.UploadCropbox} this
30642          * @param {XMLHttpRequest} xhr
30643          */
30644         "exception" : true,
30645         /**
30646          * @event beforeloadcanvas
30647          * Fire before load the canvas
30648          * @param {Roo.bootstrap.UploadCropbox} this
30649          * @param {String} src
30650          */
30651         "beforeloadcanvas" : true,
30652         /**
30653          * @event trash
30654          * Fire when trash image
30655          * @param {Roo.bootstrap.UploadCropbox} this
30656          */
30657         "trash" : true,
30658         /**
30659          * @event download
30660          * Fire when download the image
30661          * @param {Roo.bootstrap.UploadCropbox} this
30662          */
30663         "download" : true,
30664         /**
30665          * @event footerbuttonclick
30666          * Fire when footerbuttonclick
30667          * @param {Roo.bootstrap.UploadCropbox} this
30668          * @param {String} type
30669          */
30670         "footerbuttonclick" : true,
30671         /**
30672          * @event resize
30673          * Fire when resize
30674          * @param {Roo.bootstrap.UploadCropbox} this
30675          */
30676         "resize" : true,
30677         /**
30678          * @event rotate
30679          * Fire when rotate the image
30680          * @param {Roo.bootstrap.UploadCropbox} this
30681          * @param {String} pos
30682          */
30683         "rotate" : true,
30684         /**
30685          * @event inspect
30686          * Fire when inspect the file
30687          * @param {Roo.bootstrap.UploadCropbox} this
30688          * @param {Object} file
30689          */
30690         "inspect" : true,
30691         /**
30692          * @event upload
30693          * Fire when xhr upload the file
30694          * @param {Roo.bootstrap.UploadCropbox} this
30695          * @param {Object} data
30696          */
30697         "upload" : true,
30698         /**
30699          * @event arrange
30700          * Fire when arrange the file data
30701          * @param {Roo.bootstrap.UploadCropbox} this
30702          * @param {Object} formData
30703          */
30704         "arrange" : true
30705     });
30706     
30707     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30708 };
30709
30710 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30711     
30712     emptyText : 'Click to upload image',
30713     rotateNotify : 'Image is too small to rotate',
30714     errorTimeout : 3000,
30715     scale : 0,
30716     baseScale : 1,
30717     rotate : 0,
30718     dragable : false,
30719     pinching : false,
30720     mouseX : 0,
30721     mouseY : 0,
30722     cropData : false,
30723     minWidth : 300,
30724     minHeight : 300,
30725     file : false,
30726     exif : {},
30727     baseRotate : 1,
30728     cropType : 'image/jpeg',
30729     buttons : false,
30730     canvasLoaded : false,
30731     isDocument : false,
30732     method : 'POST',
30733     paramName : 'imageUpload',
30734     loadMask : true,
30735     loadingText : 'Loading...',
30736     maskEl : false,
30737     
30738     getAutoCreate : function()
30739     {
30740         var cfg = {
30741             tag : 'div',
30742             cls : 'roo-upload-cropbox',
30743             cn : [
30744                 {
30745                     tag : 'input',
30746                     cls : 'roo-upload-cropbox-selector',
30747                     type : 'file'
30748                 },
30749                 {
30750                     tag : 'div',
30751                     cls : 'roo-upload-cropbox-body',
30752                     style : 'cursor:pointer',
30753                     cn : [
30754                         {
30755                             tag : 'div',
30756                             cls : 'roo-upload-cropbox-preview'
30757                         },
30758                         {
30759                             tag : 'div',
30760                             cls : 'roo-upload-cropbox-thumb'
30761                         },
30762                         {
30763                             tag : 'div',
30764                             cls : 'roo-upload-cropbox-empty-notify',
30765                             html : this.emptyText
30766                         },
30767                         {
30768                             tag : 'div',
30769                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30770                             html : this.rotateNotify
30771                         }
30772                     ]
30773                 },
30774                 {
30775                     tag : 'div',
30776                     cls : 'roo-upload-cropbox-footer',
30777                     cn : {
30778                         tag : 'div',
30779                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30780                         cn : []
30781                     }
30782                 }
30783             ]
30784         };
30785         
30786         return cfg;
30787     },
30788     
30789     onRender : function(ct, position)
30790     {
30791         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30792         
30793         if (this.buttons.length) {
30794             
30795             Roo.each(this.buttons, function(bb) {
30796                 
30797                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30798                 
30799                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30800                 
30801             }, this);
30802         }
30803         
30804         if(this.loadMask){
30805             this.maskEl = this.el;
30806         }
30807     },
30808     
30809     initEvents : function()
30810     {
30811         this.urlAPI = (window.createObjectURL && window) || 
30812                                 (window.URL && URL.revokeObjectURL && URL) || 
30813                                 (window.webkitURL && webkitURL);
30814                         
30815         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30816         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30817         
30818         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30819         this.selectorEl.hide();
30820         
30821         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30822         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30823         
30824         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30825         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30826         this.thumbEl.hide();
30827         
30828         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30829         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30830         
30831         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30832         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30833         this.errorEl.hide();
30834         
30835         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30836         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30837         this.footerEl.hide();
30838         
30839         this.setThumbBoxSize();
30840         
30841         this.bind();
30842         
30843         this.resize();
30844         
30845         this.fireEvent('initial', this);
30846     },
30847
30848     bind : function()
30849     {
30850         var _this = this;
30851         
30852         window.addEventListener("resize", function() { _this.resize(); } );
30853         
30854         this.bodyEl.on('click', this.beforeSelectFile, this);
30855         
30856         if(Roo.isTouch){
30857             this.bodyEl.on('touchstart', this.onTouchStart, this);
30858             this.bodyEl.on('touchmove', this.onTouchMove, this);
30859             this.bodyEl.on('touchend', this.onTouchEnd, this);
30860         }
30861         
30862         if(!Roo.isTouch){
30863             this.bodyEl.on('mousedown', this.onMouseDown, this);
30864             this.bodyEl.on('mousemove', this.onMouseMove, this);
30865             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30866             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30867             Roo.get(document).on('mouseup', this.onMouseUp, this);
30868         }
30869         
30870         this.selectorEl.on('change', this.onFileSelected, this);
30871     },
30872     
30873     reset : function()
30874     {    
30875         this.scale = 0;
30876         this.baseScale = 1;
30877         this.rotate = 0;
30878         this.baseRotate = 1;
30879         this.dragable = false;
30880         this.pinching = false;
30881         this.mouseX = 0;
30882         this.mouseY = 0;
30883         this.cropData = false;
30884         this.notifyEl.dom.innerHTML = this.emptyText;
30885         
30886         this.selectorEl.dom.value = '';
30887         
30888     },
30889     
30890     resize : function()
30891     {
30892         if(this.fireEvent('resize', this) != false){
30893             this.setThumbBoxPosition();
30894             this.setCanvasPosition();
30895         }
30896     },
30897     
30898     onFooterButtonClick : function(e, el, o, type)
30899     {
30900         switch (type) {
30901             case 'rotate-left' :
30902                 this.onRotateLeft(e);
30903                 break;
30904             case 'rotate-right' :
30905                 this.onRotateRight(e);
30906                 break;
30907             case 'picture' :
30908                 this.beforeSelectFile(e);
30909                 break;
30910             case 'trash' :
30911                 this.trash(e);
30912                 break;
30913             case 'crop' :
30914                 this.crop(e);
30915                 break;
30916             case 'download' :
30917                 this.download(e);
30918                 break;
30919             default :
30920                 break;
30921         }
30922         
30923         this.fireEvent('footerbuttonclick', this, type);
30924     },
30925     
30926     beforeSelectFile : function(e)
30927     {
30928         e.preventDefault();
30929         
30930         if(this.fireEvent('beforeselectfile', this) != false){
30931             this.selectorEl.dom.click();
30932         }
30933     },
30934     
30935     onFileSelected : function(e)
30936     {
30937         e.preventDefault();
30938         
30939         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30940             return;
30941         }
30942         
30943         var file = this.selectorEl.dom.files[0];
30944         
30945         if(this.fireEvent('inspect', this, file) != false){
30946             this.prepare(file);
30947         }
30948         
30949     },
30950     
30951     trash : function(e)
30952     {
30953         this.fireEvent('trash', this);
30954     },
30955     
30956     download : function(e)
30957     {
30958         this.fireEvent('download', this);
30959     },
30960     
30961     loadCanvas : function(src)
30962     {   
30963         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30964             
30965             this.reset();
30966             
30967             this.imageEl = document.createElement('img');
30968             
30969             var _this = this;
30970             
30971             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30972             
30973             this.imageEl.src = src;
30974         }
30975     },
30976     
30977     onLoadCanvas : function()
30978     {   
30979         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30980         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30981         
30982         this.bodyEl.un('click', this.beforeSelectFile, this);
30983         
30984         this.notifyEl.hide();
30985         this.thumbEl.show();
30986         this.footerEl.show();
30987         
30988         this.baseRotateLevel();
30989         
30990         if(this.isDocument){
30991             this.setThumbBoxSize();
30992         }
30993         
30994         this.setThumbBoxPosition();
30995         
30996         this.baseScaleLevel();
30997         
30998         this.draw();
30999         
31000         this.resize();
31001         
31002         this.canvasLoaded = true;
31003         
31004         if(this.loadMask){
31005             this.maskEl.unmask();
31006         }
31007         
31008     },
31009     
31010     setCanvasPosition : function()
31011     {   
31012         if(!this.canvasEl){
31013             return;
31014         }
31015         
31016         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31017         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31018         
31019         this.previewEl.setLeft(pw);
31020         this.previewEl.setTop(ph);
31021         
31022     },
31023     
31024     onMouseDown : function(e)
31025     {   
31026         e.stopEvent();
31027         
31028         this.dragable = true;
31029         this.pinching = false;
31030         
31031         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31032             this.dragable = false;
31033             return;
31034         }
31035         
31036         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31037         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31038         
31039     },
31040     
31041     onMouseMove : function(e)
31042     {   
31043         e.stopEvent();
31044         
31045         if(!this.canvasLoaded){
31046             return;
31047         }
31048         
31049         if (!this.dragable){
31050             return;
31051         }
31052         
31053         var minX = Math.ceil(this.thumbEl.getLeft(true));
31054         var minY = Math.ceil(this.thumbEl.getTop(true));
31055         
31056         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31057         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31058         
31059         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31060         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31061         
31062         x = x - this.mouseX;
31063         y = y - this.mouseY;
31064         
31065         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31066         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31067         
31068         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31069         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31070         
31071         this.previewEl.setLeft(bgX);
31072         this.previewEl.setTop(bgY);
31073         
31074         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31075         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31076     },
31077     
31078     onMouseUp : function(e)
31079     {   
31080         e.stopEvent();
31081         
31082         this.dragable = false;
31083     },
31084     
31085     onMouseWheel : function(e)
31086     {   
31087         e.stopEvent();
31088         
31089         this.startScale = this.scale;
31090         
31091         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31092         
31093         if(!this.zoomable()){
31094             this.scale = this.startScale;
31095             return;
31096         }
31097         
31098         this.draw();
31099         
31100         return;
31101     },
31102     
31103     zoomable : function()
31104     {
31105         var minScale = this.thumbEl.getWidth() / this.minWidth;
31106         
31107         if(this.minWidth < this.minHeight){
31108             minScale = this.thumbEl.getHeight() / this.minHeight;
31109         }
31110         
31111         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31112         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31113         
31114         if(
31115                 this.isDocument &&
31116                 (this.rotate == 0 || this.rotate == 180) && 
31117                 (
31118                     width > this.imageEl.OriginWidth || 
31119                     height > this.imageEl.OriginHeight ||
31120                     (width < this.minWidth && height < this.minHeight)
31121                 )
31122         ){
31123             return false;
31124         }
31125         
31126         if(
31127                 this.isDocument &&
31128                 (this.rotate == 90 || this.rotate == 270) && 
31129                 (
31130                     width > this.imageEl.OriginWidth || 
31131                     height > this.imageEl.OriginHeight ||
31132                     (width < this.minHeight && height < this.minWidth)
31133                 )
31134         ){
31135             return false;
31136         }
31137         
31138         if(
31139                 !this.isDocument &&
31140                 (this.rotate == 0 || this.rotate == 180) && 
31141                 (
31142                     width < this.minWidth || 
31143                     width > this.imageEl.OriginWidth || 
31144                     height < this.minHeight || 
31145                     height > this.imageEl.OriginHeight
31146                 )
31147         ){
31148             return false;
31149         }
31150         
31151         if(
31152                 !this.isDocument &&
31153                 (this.rotate == 90 || this.rotate == 270) && 
31154                 (
31155                     width < this.minHeight || 
31156                     width > this.imageEl.OriginWidth || 
31157                     height < this.minWidth || 
31158                     height > this.imageEl.OriginHeight
31159                 )
31160         ){
31161             return false;
31162         }
31163         
31164         return true;
31165         
31166     },
31167     
31168     onRotateLeft : function(e)
31169     {   
31170         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31171             
31172             var minScale = this.thumbEl.getWidth() / this.minWidth;
31173             
31174             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31175             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31176             
31177             this.startScale = this.scale;
31178             
31179             while (this.getScaleLevel() < minScale){
31180             
31181                 this.scale = this.scale + 1;
31182                 
31183                 if(!this.zoomable()){
31184                     break;
31185                 }
31186                 
31187                 if(
31188                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31189                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31190                 ){
31191                     continue;
31192                 }
31193                 
31194                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31195
31196                 this.draw();
31197                 
31198                 return;
31199             }
31200             
31201             this.scale = this.startScale;
31202             
31203             this.onRotateFail();
31204             
31205             return false;
31206         }
31207         
31208         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31209
31210         if(this.isDocument){
31211             this.setThumbBoxSize();
31212             this.setThumbBoxPosition();
31213             this.setCanvasPosition();
31214         }
31215         
31216         this.draw();
31217         
31218         this.fireEvent('rotate', this, 'left');
31219         
31220     },
31221     
31222     onRotateRight : function(e)
31223     {
31224         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31225             
31226             var minScale = this.thumbEl.getWidth() / this.minWidth;
31227         
31228             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31229             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31230             
31231             this.startScale = this.scale;
31232             
31233             while (this.getScaleLevel() < minScale){
31234             
31235                 this.scale = this.scale + 1;
31236                 
31237                 if(!this.zoomable()){
31238                     break;
31239                 }
31240                 
31241                 if(
31242                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31243                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31244                 ){
31245                     continue;
31246                 }
31247                 
31248                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31249
31250                 this.draw();
31251                 
31252                 return;
31253             }
31254             
31255             this.scale = this.startScale;
31256             
31257             this.onRotateFail();
31258             
31259             return false;
31260         }
31261         
31262         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31263
31264         if(this.isDocument){
31265             this.setThumbBoxSize();
31266             this.setThumbBoxPosition();
31267             this.setCanvasPosition();
31268         }
31269         
31270         this.draw();
31271         
31272         this.fireEvent('rotate', this, 'right');
31273     },
31274     
31275     onRotateFail : function()
31276     {
31277         this.errorEl.show(true);
31278         
31279         var _this = this;
31280         
31281         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31282     },
31283     
31284     draw : function()
31285     {
31286         this.previewEl.dom.innerHTML = '';
31287         
31288         var canvasEl = document.createElement("canvas");
31289         
31290         var contextEl = canvasEl.getContext("2d");
31291         
31292         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31293         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31294         var center = this.imageEl.OriginWidth / 2;
31295         
31296         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31297             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31298             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31299             center = this.imageEl.OriginHeight / 2;
31300         }
31301         
31302         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31303         
31304         contextEl.translate(center, center);
31305         contextEl.rotate(this.rotate * Math.PI / 180);
31306
31307         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31308         
31309         this.canvasEl = document.createElement("canvas");
31310         
31311         this.contextEl = this.canvasEl.getContext("2d");
31312         
31313         switch (this.rotate) {
31314             case 0 :
31315                 
31316                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31317                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31318                 
31319                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31320                 
31321                 break;
31322             case 90 : 
31323                 
31324                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31325                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31326                 
31327                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31328                     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);
31329                     break;
31330                 }
31331                 
31332                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31333                 
31334                 break;
31335             case 180 :
31336                 
31337                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31338                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31339                 
31340                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31341                     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);
31342                     break;
31343                 }
31344                 
31345                 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);
31346                 
31347                 break;
31348             case 270 :
31349                 
31350                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31351                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31352         
31353                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31354                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31355                     break;
31356                 }
31357                 
31358                 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);
31359                 
31360                 break;
31361             default : 
31362                 break;
31363         }
31364         
31365         this.previewEl.appendChild(this.canvasEl);
31366         
31367         this.setCanvasPosition();
31368     },
31369     
31370     crop : function()
31371     {
31372         if(!this.canvasLoaded){
31373             return;
31374         }
31375         
31376         var imageCanvas = document.createElement("canvas");
31377         
31378         var imageContext = imageCanvas.getContext("2d");
31379         
31380         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31381         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31382         
31383         var center = imageCanvas.width / 2;
31384         
31385         imageContext.translate(center, center);
31386         
31387         imageContext.rotate(this.rotate * Math.PI / 180);
31388         
31389         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31390         
31391         var canvas = document.createElement("canvas");
31392         
31393         var context = canvas.getContext("2d");
31394                 
31395         canvas.width = this.minWidth;
31396         canvas.height = this.minHeight;
31397
31398         switch (this.rotate) {
31399             case 0 :
31400                 
31401                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31402                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31403                 
31404                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31405                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31406                 
31407                 var targetWidth = this.minWidth - 2 * x;
31408                 var targetHeight = this.minHeight - 2 * y;
31409                 
31410                 var scale = 1;
31411                 
31412                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31413                     scale = targetWidth / width;
31414                 }
31415                 
31416                 if(x > 0 && y == 0){
31417                     scale = targetHeight / height;
31418                 }
31419                 
31420                 if(x > 0 && y > 0){
31421                     scale = targetWidth / width;
31422                     
31423                     if(width < height){
31424                         scale = targetHeight / height;
31425                     }
31426                 }
31427                 
31428                 context.scale(scale, scale);
31429                 
31430                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31431                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31432
31433                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31434                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31435
31436                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31437                 
31438                 break;
31439             case 90 : 
31440                 
31441                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31442                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31443                 
31444                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31445                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31446                 
31447                 var targetWidth = this.minWidth - 2 * x;
31448                 var targetHeight = this.minHeight - 2 * y;
31449                 
31450                 var scale = 1;
31451                 
31452                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31453                     scale = targetWidth / width;
31454                 }
31455                 
31456                 if(x > 0 && y == 0){
31457                     scale = targetHeight / height;
31458                 }
31459                 
31460                 if(x > 0 && y > 0){
31461                     scale = targetWidth / width;
31462                     
31463                     if(width < height){
31464                         scale = targetHeight / height;
31465                     }
31466                 }
31467                 
31468                 context.scale(scale, scale);
31469                 
31470                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31471                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31472
31473                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31474                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31475                 
31476                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31477                 
31478                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31479                 
31480                 break;
31481             case 180 :
31482                 
31483                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31484                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31485                 
31486                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31487                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31488                 
31489                 var targetWidth = this.minWidth - 2 * x;
31490                 var targetHeight = this.minHeight - 2 * y;
31491                 
31492                 var scale = 1;
31493                 
31494                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31495                     scale = targetWidth / width;
31496                 }
31497                 
31498                 if(x > 0 && y == 0){
31499                     scale = targetHeight / height;
31500                 }
31501                 
31502                 if(x > 0 && y > 0){
31503                     scale = targetWidth / width;
31504                     
31505                     if(width < height){
31506                         scale = targetHeight / height;
31507                     }
31508                 }
31509                 
31510                 context.scale(scale, scale);
31511                 
31512                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31513                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31514
31515                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31516                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31517
31518                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31519                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31520                 
31521                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31522                 
31523                 break;
31524             case 270 :
31525                 
31526                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31527                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31528                 
31529                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31530                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31531                 
31532                 var targetWidth = this.minWidth - 2 * x;
31533                 var targetHeight = this.minHeight - 2 * y;
31534                 
31535                 var scale = 1;
31536                 
31537                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31538                     scale = targetWidth / width;
31539                 }
31540                 
31541                 if(x > 0 && y == 0){
31542                     scale = targetHeight / height;
31543                 }
31544                 
31545                 if(x > 0 && y > 0){
31546                     scale = targetWidth / width;
31547                     
31548                     if(width < height){
31549                         scale = targetHeight / height;
31550                     }
31551                 }
31552                 
31553                 context.scale(scale, scale);
31554                 
31555                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31556                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31557
31558                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31559                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31560                 
31561                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31562                 
31563                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31564                 
31565                 break;
31566             default : 
31567                 break;
31568         }
31569         
31570         this.cropData = canvas.toDataURL(this.cropType);
31571         
31572         if(this.fireEvent('crop', this, this.cropData) !== false){
31573             this.process(this.file, this.cropData);
31574         }
31575         
31576         return;
31577         
31578     },
31579     
31580     setThumbBoxSize : function()
31581     {
31582         var width, height;
31583         
31584         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31585             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31586             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31587             
31588             this.minWidth = width;
31589             this.minHeight = height;
31590             
31591             if(this.rotate == 90 || this.rotate == 270){
31592                 this.minWidth = height;
31593                 this.minHeight = width;
31594             }
31595         }
31596         
31597         height = 300;
31598         width = Math.ceil(this.minWidth * height / this.minHeight);
31599         
31600         if(this.minWidth > this.minHeight){
31601             width = 300;
31602             height = Math.ceil(this.minHeight * width / this.minWidth);
31603         }
31604         
31605         this.thumbEl.setStyle({
31606             width : width + 'px',
31607             height : height + 'px'
31608         });
31609
31610         return;
31611             
31612     },
31613     
31614     setThumbBoxPosition : function()
31615     {
31616         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31617         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31618         
31619         this.thumbEl.setLeft(x);
31620         this.thumbEl.setTop(y);
31621         
31622     },
31623     
31624     baseRotateLevel : function()
31625     {
31626         this.baseRotate = 1;
31627         
31628         if(
31629                 typeof(this.exif) != 'undefined' &&
31630                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31631                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31632         ){
31633             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31634         }
31635         
31636         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31637         
31638     },
31639     
31640     baseScaleLevel : function()
31641     {
31642         var width, height;
31643         
31644         if(this.isDocument){
31645             
31646             if(this.baseRotate == 6 || this.baseRotate == 8){
31647             
31648                 height = this.thumbEl.getHeight();
31649                 this.baseScale = height / this.imageEl.OriginWidth;
31650
31651                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31652                     width = this.thumbEl.getWidth();
31653                     this.baseScale = width / this.imageEl.OriginHeight;
31654                 }
31655
31656                 return;
31657             }
31658
31659             height = this.thumbEl.getHeight();
31660             this.baseScale = height / this.imageEl.OriginHeight;
31661
31662             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31663                 width = this.thumbEl.getWidth();
31664                 this.baseScale = width / this.imageEl.OriginWidth;
31665             }
31666
31667             return;
31668         }
31669         
31670         if(this.baseRotate == 6 || this.baseRotate == 8){
31671             
31672             width = this.thumbEl.getHeight();
31673             this.baseScale = width / this.imageEl.OriginHeight;
31674             
31675             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31676                 height = this.thumbEl.getWidth();
31677                 this.baseScale = height / this.imageEl.OriginHeight;
31678             }
31679             
31680             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31681                 height = this.thumbEl.getWidth();
31682                 this.baseScale = height / this.imageEl.OriginHeight;
31683                 
31684                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31685                     width = this.thumbEl.getHeight();
31686                     this.baseScale = width / this.imageEl.OriginWidth;
31687                 }
31688             }
31689             
31690             return;
31691         }
31692         
31693         width = this.thumbEl.getWidth();
31694         this.baseScale = width / this.imageEl.OriginWidth;
31695         
31696         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31697             height = this.thumbEl.getHeight();
31698             this.baseScale = height / this.imageEl.OriginHeight;
31699         }
31700         
31701         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31702             
31703             height = this.thumbEl.getHeight();
31704             this.baseScale = height / this.imageEl.OriginHeight;
31705             
31706             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31707                 width = this.thumbEl.getWidth();
31708                 this.baseScale = width / this.imageEl.OriginWidth;
31709             }
31710             
31711         }
31712         
31713         return;
31714     },
31715     
31716     getScaleLevel : function()
31717     {
31718         return this.baseScale * Math.pow(1.1, this.scale);
31719     },
31720     
31721     onTouchStart : function(e)
31722     {
31723         if(!this.canvasLoaded){
31724             this.beforeSelectFile(e);
31725             return;
31726         }
31727         
31728         var touches = e.browserEvent.touches;
31729         
31730         if(!touches){
31731             return;
31732         }
31733         
31734         if(touches.length == 1){
31735             this.onMouseDown(e);
31736             return;
31737         }
31738         
31739         if(touches.length != 2){
31740             return;
31741         }
31742         
31743         var coords = [];
31744         
31745         for(var i = 0, finger; finger = touches[i]; i++){
31746             coords.push(finger.pageX, finger.pageY);
31747         }
31748         
31749         var x = Math.pow(coords[0] - coords[2], 2);
31750         var y = Math.pow(coords[1] - coords[3], 2);
31751         
31752         this.startDistance = Math.sqrt(x + y);
31753         
31754         this.startScale = this.scale;
31755         
31756         this.pinching = true;
31757         this.dragable = false;
31758         
31759     },
31760     
31761     onTouchMove : function(e)
31762     {
31763         if(!this.pinching && !this.dragable){
31764             return;
31765         }
31766         
31767         var touches = e.browserEvent.touches;
31768         
31769         if(!touches){
31770             return;
31771         }
31772         
31773         if(this.dragable){
31774             this.onMouseMove(e);
31775             return;
31776         }
31777         
31778         var coords = [];
31779         
31780         for(var i = 0, finger; finger = touches[i]; i++){
31781             coords.push(finger.pageX, finger.pageY);
31782         }
31783         
31784         var x = Math.pow(coords[0] - coords[2], 2);
31785         var y = Math.pow(coords[1] - coords[3], 2);
31786         
31787         this.endDistance = Math.sqrt(x + y);
31788         
31789         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31790         
31791         if(!this.zoomable()){
31792             this.scale = this.startScale;
31793             return;
31794         }
31795         
31796         this.draw();
31797         
31798     },
31799     
31800     onTouchEnd : function(e)
31801     {
31802         this.pinching = false;
31803         this.dragable = false;
31804         
31805     },
31806     
31807     process : function(file, crop)
31808     {
31809         if(this.loadMask){
31810             this.maskEl.mask(this.loadingText);
31811         }
31812         
31813         this.xhr = new XMLHttpRequest();
31814         
31815         file.xhr = this.xhr;
31816
31817         this.xhr.open(this.method, this.url, true);
31818         
31819         var headers = {
31820             "Accept": "application/json",
31821             "Cache-Control": "no-cache",
31822             "X-Requested-With": "XMLHttpRequest"
31823         };
31824         
31825         for (var headerName in headers) {
31826             var headerValue = headers[headerName];
31827             if (headerValue) {
31828                 this.xhr.setRequestHeader(headerName, headerValue);
31829             }
31830         }
31831         
31832         var _this = this;
31833         
31834         this.xhr.onload = function()
31835         {
31836             _this.xhrOnLoad(_this.xhr);
31837         }
31838         
31839         this.xhr.onerror = function()
31840         {
31841             _this.xhrOnError(_this.xhr);
31842         }
31843         
31844         var formData = new FormData();
31845
31846         formData.append('returnHTML', 'NO');
31847         
31848         if(crop){
31849             formData.append('crop', crop);
31850         }
31851         
31852         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31853             formData.append(this.paramName, file, file.name);
31854         }
31855         
31856         if(typeof(file.filename) != 'undefined'){
31857             formData.append('filename', file.filename);
31858         }
31859         
31860         if(typeof(file.mimetype) != 'undefined'){
31861             formData.append('mimetype', file.mimetype);
31862         }
31863         
31864         if(this.fireEvent('arrange', this, formData) != false){
31865             this.xhr.send(formData);
31866         };
31867     },
31868     
31869     xhrOnLoad : function(xhr)
31870     {
31871         if(this.loadMask){
31872             this.maskEl.unmask();
31873         }
31874         
31875         if (xhr.readyState !== 4) {
31876             this.fireEvent('exception', this, xhr);
31877             return;
31878         }
31879
31880         var response = Roo.decode(xhr.responseText);
31881         
31882         if(!response.success){
31883             this.fireEvent('exception', this, xhr);
31884             return;
31885         }
31886         
31887         var response = Roo.decode(xhr.responseText);
31888         
31889         this.fireEvent('upload', this, response);
31890         
31891     },
31892     
31893     xhrOnError : function()
31894     {
31895         if(this.loadMask){
31896             this.maskEl.unmask();
31897         }
31898         
31899         Roo.log('xhr on error');
31900         
31901         var response = Roo.decode(xhr.responseText);
31902           
31903         Roo.log(response);
31904         
31905     },
31906     
31907     prepare : function(file)
31908     {   
31909         if(this.loadMask){
31910             this.maskEl.mask(this.loadingText);
31911         }
31912         
31913         this.file = false;
31914         this.exif = {};
31915         
31916         if(typeof(file) === 'string'){
31917             this.loadCanvas(file);
31918             return;
31919         }
31920         
31921         if(!file || !this.urlAPI){
31922             return;
31923         }
31924         
31925         this.file = file;
31926         this.cropType = file.type;
31927         
31928         var _this = this;
31929         
31930         if(this.fireEvent('prepare', this, this.file) != false){
31931             
31932             var reader = new FileReader();
31933             
31934             reader.onload = function (e) {
31935                 if (e.target.error) {
31936                     Roo.log(e.target.error);
31937                     return;
31938                 }
31939                 
31940                 var buffer = e.target.result,
31941                     dataView = new DataView(buffer),
31942                     offset = 2,
31943                     maxOffset = dataView.byteLength - 4,
31944                     markerBytes,
31945                     markerLength;
31946                 
31947                 if (dataView.getUint16(0) === 0xffd8) {
31948                     while (offset < maxOffset) {
31949                         markerBytes = dataView.getUint16(offset);
31950                         
31951                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31952                             markerLength = dataView.getUint16(offset + 2) + 2;
31953                             if (offset + markerLength > dataView.byteLength) {
31954                                 Roo.log('Invalid meta data: Invalid segment size.');
31955                                 break;
31956                             }
31957                             
31958                             if(markerBytes == 0xffe1){
31959                                 _this.parseExifData(
31960                                     dataView,
31961                                     offset,
31962                                     markerLength
31963                                 );
31964                             }
31965                             
31966                             offset += markerLength;
31967                             
31968                             continue;
31969                         }
31970                         
31971                         break;
31972                     }
31973                     
31974                 }
31975                 
31976                 var url = _this.urlAPI.createObjectURL(_this.file);
31977                 
31978                 _this.loadCanvas(url);
31979                 
31980                 return;
31981             }
31982             
31983             reader.readAsArrayBuffer(this.file);
31984             
31985         }
31986         
31987     },
31988     
31989     parseExifData : function(dataView, offset, length)
31990     {
31991         var tiffOffset = offset + 10,
31992             littleEndian,
31993             dirOffset;
31994     
31995         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31996             // No Exif data, might be XMP data instead
31997             return;
31998         }
31999         
32000         // Check for the ASCII code for "Exif" (0x45786966):
32001         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32002             // No Exif data, might be XMP data instead
32003             return;
32004         }
32005         if (tiffOffset + 8 > dataView.byteLength) {
32006             Roo.log('Invalid Exif data: Invalid segment size.');
32007             return;
32008         }
32009         // Check for the two null bytes:
32010         if (dataView.getUint16(offset + 8) !== 0x0000) {
32011             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32012             return;
32013         }
32014         // Check the byte alignment:
32015         switch (dataView.getUint16(tiffOffset)) {
32016         case 0x4949:
32017             littleEndian = true;
32018             break;
32019         case 0x4D4D:
32020             littleEndian = false;
32021             break;
32022         default:
32023             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32024             return;
32025         }
32026         // Check for the TIFF tag marker (0x002A):
32027         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32028             Roo.log('Invalid Exif data: Missing TIFF marker.');
32029             return;
32030         }
32031         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32032         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32033         
32034         this.parseExifTags(
32035             dataView,
32036             tiffOffset,
32037             tiffOffset + dirOffset,
32038             littleEndian
32039         );
32040     },
32041     
32042     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32043     {
32044         var tagsNumber,
32045             dirEndOffset,
32046             i;
32047         if (dirOffset + 6 > dataView.byteLength) {
32048             Roo.log('Invalid Exif data: Invalid directory offset.');
32049             return;
32050         }
32051         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32052         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32053         if (dirEndOffset + 4 > dataView.byteLength) {
32054             Roo.log('Invalid Exif data: Invalid directory size.');
32055             return;
32056         }
32057         for (i = 0; i < tagsNumber; i += 1) {
32058             this.parseExifTag(
32059                 dataView,
32060                 tiffOffset,
32061                 dirOffset + 2 + 12 * i, // tag offset
32062                 littleEndian
32063             );
32064         }
32065         // Return the offset to the next directory:
32066         return dataView.getUint32(dirEndOffset, littleEndian);
32067     },
32068     
32069     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32070     {
32071         var tag = dataView.getUint16(offset, littleEndian);
32072         
32073         this.exif[tag] = this.getExifValue(
32074             dataView,
32075             tiffOffset,
32076             offset,
32077             dataView.getUint16(offset + 2, littleEndian), // tag type
32078             dataView.getUint32(offset + 4, littleEndian), // tag length
32079             littleEndian
32080         );
32081     },
32082     
32083     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32084     {
32085         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32086             tagSize,
32087             dataOffset,
32088             values,
32089             i,
32090             str,
32091             c;
32092     
32093         if (!tagType) {
32094             Roo.log('Invalid Exif data: Invalid tag type.');
32095             return;
32096         }
32097         
32098         tagSize = tagType.size * length;
32099         // Determine if the value is contained in the dataOffset bytes,
32100         // or if the value at the dataOffset is a pointer to the actual data:
32101         dataOffset = tagSize > 4 ?
32102                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32103         if (dataOffset + tagSize > dataView.byteLength) {
32104             Roo.log('Invalid Exif data: Invalid data offset.');
32105             return;
32106         }
32107         if (length === 1) {
32108             return tagType.getValue(dataView, dataOffset, littleEndian);
32109         }
32110         values = [];
32111         for (i = 0; i < length; i += 1) {
32112             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32113         }
32114         
32115         if (tagType.ascii) {
32116             str = '';
32117             // Concatenate the chars:
32118             for (i = 0; i < values.length; i += 1) {
32119                 c = values[i];
32120                 // Ignore the terminating NULL byte(s):
32121                 if (c === '\u0000') {
32122                     break;
32123                 }
32124                 str += c;
32125             }
32126             return str;
32127         }
32128         return values;
32129     }
32130     
32131 });
32132
32133 Roo.apply(Roo.bootstrap.UploadCropbox, {
32134     tags : {
32135         'Orientation': 0x0112
32136     },
32137     
32138     Orientation: {
32139             1: 0, //'top-left',
32140 //            2: 'top-right',
32141             3: 180, //'bottom-right',
32142 //            4: 'bottom-left',
32143 //            5: 'left-top',
32144             6: 90, //'right-top',
32145 //            7: 'right-bottom',
32146             8: 270 //'left-bottom'
32147     },
32148     
32149     exifTagTypes : {
32150         // byte, 8-bit unsigned int:
32151         1: {
32152             getValue: function (dataView, dataOffset) {
32153                 return dataView.getUint8(dataOffset);
32154             },
32155             size: 1
32156         },
32157         // ascii, 8-bit byte:
32158         2: {
32159             getValue: function (dataView, dataOffset) {
32160                 return String.fromCharCode(dataView.getUint8(dataOffset));
32161             },
32162             size: 1,
32163             ascii: true
32164         },
32165         // short, 16 bit int:
32166         3: {
32167             getValue: function (dataView, dataOffset, littleEndian) {
32168                 return dataView.getUint16(dataOffset, littleEndian);
32169             },
32170             size: 2
32171         },
32172         // long, 32 bit int:
32173         4: {
32174             getValue: function (dataView, dataOffset, littleEndian) {
32175                 return dataView.getUint32(dataOffset, littleEndian);
32176             },
32177             size: 4
32178         },
32179         // rational = two long values, first is numerator, second is denominator:
32180         5: {
32181             getValue: function (dataView, dataOffset, littleEndian) {
32182                 return dataView.getUint32(dataOffset, littleEndian) /
32183                     dataView.getUint32(dataOffset + 4, littleEndian);
32184             },
32185             size: 8
32186         },
32187         // slong, 32 bit signed int:
32188         9: {
32189             getValue: function (dataView, dataOffset, littleEndian) {
32190                 return dataView.getInt32(dataOffset, littleEndian);
32191             },
32192             size: 4
32193         },
32194         // srational, two slongs, first is numerator, second is denominator:
32195         10: {
32196             getValue: function (dataView, dataOffset, littleEndian) {
32197                 return dataView.getInt32(dataOffset, littleEndian) /
32198                     dataView.getInt32(dataOffset + 4, littleEndian);
32199             },
32200             size: 8
32201         }
32202     },
32203     
32204     footer : {
32205         STANDARD : [
32206             {
32207                 tag : 'div',
32208                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32209                 action : 'rotate-left',
32210                 cn : [
32211                     {
32212                         tag : 'button',
32213                         cls : 'btn btn-default',
32214                         html : '<i class="fa fa-undo"></i>'
32215                     }
32216                 ]
32217             },
32218             {
32219                 tag : 'div',
32220                 cls : 'btn-group roo-upload-cropbox-picture',
32221                 action : 'picture',
32222                 cn : [
32223                     {
32224                         tag : 'button',
32225                         cls : 'btn btn-default',
32226                         html : '<i class="fa fa-picture-o"></i>'
32227                     }
32228                 ]
32229             },
32230             {
32231                 tag : 'div',
32232                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32233                 action : 'rotate-right',
32234                 cn : [
32235                     {
32236                         tag : 'button',
32237                         cls : 'btn btn-default',
32238                         html : '<i class="fa fa-repeat"></i>'
32239                     }
32240                 ]
32241             }
32242         ],
32243         DOCUMENT : [
32244             {
32245                 tag : 'div',
32246                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32247                 action : 'rotate-left',
32248                 cn : [
32249                     {
32250                         tag : 'button',
32251                         cls : 'btn btn-default',
32252                         html : '<i class="fa fa-undo"></i>'
32253                     }
32254                 ]
32255             },
32256             {
32257                 tag : 'div',
32258                 cls : 'btn-group roo-upload-cropbox-download',
32259                 action : 'download',
32260                 cn : [
32261                     {
32262                         tag : 'button',
32263                         cls : 'btn btn-default',
32264                         html : '<i class="fa fa-download"></i>'
32265                     }
32266                 ]
32267             },
32268             {
32269                 tag : 'div',
32270                 cls : 'btn-group roo-upload-cropbox-crop',
32271                 action : 'crop',
32272                 cn : [
32273                     {
32274                         tag : 'button',
32275                         cls : 'btn btn-default',
32276                         html : '<i class="fa fa-crop"></i>'
32277                     }
32278                 ]
32279             },
32280             {
32281                 tag : 'div',
32282                 cls : 'btn-group roo-upload-cropbox-trash',
32283                 action : 'trash',
32284                 cn : [
32285                     {
32286                         tag : 'button',
32287                         cls : 'btn btn-default',
32288                         html : '<i class="fa fa-trash"></i>'
32289                     }
32290                 ]
32291             },
32292             {
32293                 tag : 'div',
32294                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32295                 action : 'rotate-right',
32296                 cn : [
32297                     {
32298                         tag : 'button',
32299                         cls : 'btn btn-default',
32300                         html : '<i class="fa fa-repeat"></i>'
32301                     }
32302                 ]
32303             }
32304         ],
32305         ROTATOR : [
32306             {
32307                 tag : 'div',
32308                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32309                 action : 'rotate-left',
32310                 cn : [
32311                     {
32312                         tag : 'button',
32313                         cls : 'btn btn-default',
32314                         html : '<i class="fa fa-undo"></i>'
32315                     }
32316                 ]
32317             },
32318             {
32319                 tag : 'div',
32320                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32321                 action : 'rotate-right',
32322                 cn : [
32323                     {
32324                         tag : 'button',
32325                         cls : 'btn btn-default',
32326                         html : '<i class="fa fa-repeat"></i>'
32327                     }
32328                 ]
32329             }
32330         ]
32331     }
32332 });
32333
32334 /*
32335 * Licence: LGPL
32336 */
32337
32338 /**
32339  * @class Roo.bootstrap.DocumentManager
32340  * @extends Roo.bootstrap.Component
32341  * Bootstrap DocumentManager class
32342  * @cfg {String} paramName default 'imageUpload'
32343  * @cfg {String} toolTipName default 'filename'
32344  * @cfg {String} method default POST
32345  * @cfg {String} url action url
32346  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32347  * @cfg {Boolean} multiple multiple upload default true
32348  * @cfg {Number} thumbSize default 300
32349  * @cfg {String} fieldLabel
32350  * @cfg {Number} labelWidth default 4
32351  * @cfg {String} labelAlign (left|top) default left
32352  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32353 * @cfg {Number} labellg set the width of label (1-12)
32354  * @cfg {Number} labelmd set the width of label (1-12)
32355  * @cfg {Number} labelsm set the width of label (1-12)
32356  * @cfg {Number} labelxs set the width of label (1-12)
32357  * 
32358  * @constructor
32359  * Create a new DocumentManager
32360  * @param {Object} config The config object
32361  */
32362
32363 Roo.bootstrap.DocumentManager = function(config){
32364     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32365     
32366     this.files = [];
32367     this.delegates = [];
32368     
32369     this.addEvents({
32370         /**
32371          * @event initial
32372          * Fire when initial the DocumentManager
32373          * @param {Roo.bootstrap.DocumentManager} this
32374          */
32375         "initial" : true,
32376         /**
32377          * @event inspect
32378          * inspect selected file
32379          * @param {Roo.bootstrap.DocumentManager} this
32380          * @param {File} file
32381          */
32382         "inspect" : true,
32383         /**
32384          * @event exception
32385          * Fire when xhr load exception
32386          * @param {Roo.bootstrap.DocumentManager} this
32387          * @param {XMLHttpRequest} xhr
32388          */
32389         "exception" : true,
32390         /**
32391          * @event afterupload
32392          * Fire when xhr load exception
32393          * @param {Roo.bootstrap.DocumentManager} this
32394          * @param {XMLHttpRequest} xhr
32395          */
32396         "afterupload" : true,
32397         /**
32398          * @event prepare
32399          * prepare the form data
32400          * @param {Roo.bootstrap.DocumentManager} this
32401          * @param {Object} formData
32402          */
32403         "prepare" : true,
32404         /**
32405          * @event remove
32406          * Fire when remove the file
32407          * @param {Roo.bootstrap.DocumentManager} this
32408          * @param {Object} file
32409          */
32410         "remove" : true,
32411         /**
32412          * @event refresh
32413          * Fire after refresh the file
32414          * @param {Roo.bootstrap.DocumentManager} this
32415          */
32416         "refresh" : true,
32417         /**
32418          * @event click
32419          * Fire after click the image
32420          * @param {Roo.bootstrap.DocumentManager} this
32421          * @param {Object} file
32422          */
32423         "click" : true,
32424         /**
32425          * @event edit
32426          * Fire when upload a image and editable set to true
32427          * @param {Roo.bootstrap.DocumentManager} this
32428          * @param {Object} file
32429          */
32430         "edit" : true,
32431         /**
32432          * @event beforeselectfile
32433          * Fire before select file
32434          * @param {Roo.bootstrap.DocumentManager} this
32435          */
32436         "beforeselectfile" : true,
32437         /**
32438          * @event process
32439          * Fire before process file
32440          * @param {Roo.bootstrap.DocumentManager} this
32441          * @param {Object} file
32442          */
32443         "process" : true,
32444         /**
32445          * @event previewrendered
32446          * Fire when preview rendered
32447          * @param {Roo.bootstrap.DocumentManager} this
32448          * @param {Object} file
32449          */
32450         "previewrendered" : true,
32451         /**
32452          */
32453         "previewResize" : true
32454         
32455     });
32456 };
32457
32458 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32459     
32460     boxes : 0,
32461     inputName : '',
32462     thumbSize : 300,
32463     multiple : true,
32464     files : false,
32465     method : 'POST',
32466     url : '',
32467     paramName : 'imageUpload',
32468     toolTipName : 'filename',
32469     fieldLabel : '',
32470     labelWidth : 4,
32471     labelAlign : 'left',
32472     editable : true,
32473     delegates : false,
32474     xhr : false, 
32475     
32476     labellg : 0,
32477     labelmd : 0,
32478     labelsm : 0,
32479     labelxs : 0,
32480     
32481     getAutoCreate : function()
32482     {   
32483         var managerWidget = {
32484             tag : 'div',
32485             cls : 'roo-document-manager',
32486             cn : [
32487                 {
32488                     tag : 'input',
32489                     cls : 'roo-document-manager-selector',
32490                     type : 'file'
32491                 },
32492                 {
32493                     tag : 'div',
32494                     cls : 'roo-document-manager-uploader',
32495                     cn : [
32496                         {
32497                             tag : 'div',
32498                             cls : 'roo-document-manager-upload-btn',
32499                             html : '<i class="fa fa-plus"></i>'
32500                         }
32501                     ]
32502                     
32503                 }
32504             ]
32505         };
32506         
32507         var content = [
32508             {
32509                 tag : 'div',
32510                 cls : 'column col-md-12',
32511                 cn : managerWidget
32512             }
32513         ];
32514         
32515         if(this.fieldLabel.length){
32516             
32517             content = [
32518                 {
32519                     tag : 'div',
32520                     cls : 'column col-md-12',
32521                     html : this.fieldLabel
32522                 },
32523                 {
32524                     tag : 'div',
32525                     cls : 'column col-md-12',
32526                     cn : managerWidget
32527                 }
32528             ];
32529
32530             if(this.labelAlign == 'left'){
32531                 content = [
32532                     {
32533                         tag : 'div',
32534                         cls : 'column',
32535                         html : this.fieldLabel
32536                     },
32537                     {
32538                         tag : 'div',
32539                         cls : 'column',
32540                         cn : managerWidget
32541                     }
32542                 ];
32543                 
32544                 if(this.labelWidth > 12){
32545                     content[0].style = "width: " + this.labelWidth + 'px';
32546                 }
32547
32548                 if(this.labelWidth < 13 && this.labelmd == 0){
32549                     this.labelmd = this.labelWidth;
32550                 }
32551
32552                 if(this.labellg > 0){
32553                     content[0].cls += ' col-lg-' + this.labellg;
32554                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32555                 }
32556
32557                 if(this.labelmd > 0){
32558                     content[0].cls += ' col-md-' + this.labelmd;
32559                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32560                 }
32561
32562                 if(this.labelsm > 0){
32563                     content[0].cls += ' col-sm-' + this.labelsm;
32564                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32565                 }
32566
32567                 if(this.labelxs > 0){
32568                     content[0].cls += ' col-xs-' + this.labelxs;
32569                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32570                 }
32571                 
32572             }
32573         }
32574         
32575         var cfg = {
32576             tag : 'div',
32577             cls : 'row clearfix',
32578             cn : content
32579         };
32580         
32581         return cfg;
32582         
32583     },
32584     
32585     initEvents : function()
32586     {
32587         this.managerEl = this.el.select('.roo-document-manager', true).first();
32588         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32589         
32590         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32591         this.selectorEl.hide();
32592         
32593         if(this.multiple){
32594             this.selectorEl.attr('multiple', 'multiple');
32595         }
32596         
32597         this.selectorEl.on('change', this.onFileSelected, this);
32598         
32599         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32600         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32601         
32602         this.uploader.on('click', this.onUploaderClick, this);
32603         
32604         this.renderProgressDialog();
32605         
32606         var _this = this;
32607         
32608         window.addEventListener("resize", function() { _this.refresh(); } );
32609         
32610         this.fireEvent('initial', this);
32611     },
32612     
32613     renderProgressDialog : function()
32614     {
32615         var _this = this;
32616         
32617         this.progressDialog = new Roo.bootstrap.Modal({
32618             cls : 'roo-document-manager-progress-dialog',
32619             allow_close : false,
32620             animate : false,
32621             title : '',
32622             buttons : [
32623                 {
32624                     name  :'cancel',
32625                     weight : 'danger',
32626                     html : 'Cancel'
32627                 }
32628             ], 
32629             listeners : { 
32630                 btnclick : function() {
32631                     _this.uploadCancel();
32632                     this.hide();
32633                 }
32634             }
32635         });
32636          
32637         this.progressDialog.render(Roo.get(document.body));
32638          
32639         this.progress = new Roo.bootstrap.Progress({
32640             cls : 'roo-document-manager-progress',
32641             active : true,
32642             striped : true
32643         });
32644         
32645         this.progress.render(this.progressDialog.getChildContainer());
32646         
32647         this.progressBar = new Roo.bootstrap.ProgressBar({
32648             cls : 'roo-document-manager-progress-bar',
32649             aria_valuenow : 0,
32650             aria_valuemin : 0,
32651             aria_valuemax : 12,
32652             panel : 'success'
32653         });
32654         
32655         this.progressBar.render(this.progress.getChildContainer());
32656     },
32657     
32658     onUploaderClick : function(e)
32659     {
32660         e.preventDefault();
32661      
32662         if(this.fireEvent('beforeselectfile', this) != false){
32663             this.selectorEl.dom.click();
32664         }
32665         
32666     },
32667     
32668     onFileSelected : function(e)
32669     {
32670         e.preventDefault();
32671         
32672         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32673             return;
32674         }
32675         
32676         Roo.each(this.selectorEl.dom.files, function(file){
32677             if(this.fireEvent('inspect', this, file) != false){
32678                 this.files.push(file);
32679             }
32680         }, this);
32681         
32682         this.queue();
32683         
32684     },
32685     
32686     queue : function()
32687     {
32688         this.selectorEl.dom.value = '';
32689         
32690         if(!this.files || !this.files.length){
32691             return;
32692         }
32693         
32694         if(this.boxes > 0 && this.files.length > this.boxes){
32695             this.files = this.files.slice(0, this.boxes);
32696         }
32697         
32698         this.uploader.show();
32699         
32700         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32701             this.uploader.hide();
32702         }
32703         
32704         var _this = this;
32705         
32706         var files = [];
32707         
32708         var docs = [];
32709         
32710         Roo.each(this.files, function(file){
32711             
32712             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32713                 var f = this.renderPreview(file);
32714                 files.push(f);
32715                 return;
32716             }
32717             
32718             if(file.type.indexOf('image') != -1){
32719                 this.delegates.push(
32720                     (function(){
32721                         _this.process(file);
32722                     }).createDelegate(this)
32723                 );
32724         
32725                 return;
32726             }
32727             
32728             docs.push(
32729                 (function(){
32730                     _this.process(file);
32731                 }).createDelegate(this)
32732             );
32733             
32734         }, this);
32735         
32736         this.files = files;
32737         
32738         this.delegates = this.delegates.concat(docs);
32739         
32740         if(!this.delegates.length){
32741             this.refresh();
32742             return;
32743         }
32744         
32745         this.progressBar.aria_valuemax = this.delegates.length;
32746         
32747         this.arrange();
32748         
32749         return;
32750     },
32751     
32752     arrange : function()
32753     {
32754         if(!this.delegates.length){
32755             this.progressDialog.hide();
32756             this.refresh();
32757             return;
32758         }
32759         
32760         var delegate = this.delegates.shift();
32761         
32762         this.progressDialog.show();
32763         
32764         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32765         
32766         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32767         
32768         delegate();
32769     },
32770     
32771     refresh : function()
32772     {
32773         this.uploader.show();
32774         
32775         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32776             this.uploader.hide();
32777         }
32778         
32779         Roo.isTouch ? this.closable(false) : this.closable(true);
32780         
32781         this.fireEvent('refresh', this);
32782     },
32783     
32784     onRemove : function(e, el, o)
32785     {
32786         e.preventDefault();
32787         
32788         this.fireEvent('remove', this, o);
32789         
32790     },
32791     
32792     remove : function(o)
32793     {
32794         var files = [];
32795         
32796         Roo.each(this.files, function(file){
32797             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32798                 files.push(file);
32799                 return;
32800             }
32801
32802             o.target.remove();
32803
32804         }, this);
32805         
32806         this.files = files;
32807         
32808         this.refresh();
32809     },
32810     
32811     clear : function()
32812     {
32813         Roo.each(this.files, function(file){
32814             if(!file.target){
32815                 return;
32816             }
32817             
32818             file.target.remove();
32819
32820         }, this);
32821         
32822         this.files = [];
32823         
32824         this.refresh();
32825     },
32826     
32827     onClick : function(e, el, o)
32828     {
32829         e.preventDefault();
32830         
32831         this.fireEvent('click', this, o);
32832         
32833     },
32834     
32835     closable : function(closable)
32836     {
32837         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32838             
32839             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32840             
32841             if(closable){
32842                 el.show();
32843                 return;
32844             }
32845             
32846             el.hide();
32847             
32848         }, this);
32849     },
32850     
32851     xhrOnLoad : function(xhr)
32852     {
32853         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32854             el.remove();
32855         }, this);
32856         
32857         if (xhr.readyState !== 4) {
32858             this.arrange();
32859             this.fireEvent('exception', this, xhr);
32860             return;
32861         }
32862
32863         var response = Roo.decode(xhr.responseText);
32864         
32865         if(!response.success){
32866             this.arrange();
32867             this.fireEvent('exception', this, xhr);
32868             return;
32869         }
32870         
32871         var file = this.renderPreview(response.data);
32872         
32873         this.files.push(file);
32874         
32875         this.arrange();
32876         
32877         this.fireEvent('afterupload', this, xhr);
32878         
32879     },
32880     
32881     xhrOnError : function(xhr)
32882     {
32883         Roo.log('xhr on error');
32884         
32885         var response = Roo.decode(xhr.responseText);
32886           
32887         Roo.log(response);
32888         
32889         this.arrange();
32890     },
32891     
32892     process : function(file)
32893     {
32894         if(this.fireEvent('process', this, file) !== false){
32895             if(this.editable && file.type.indexOf('image') != -1){
32896                 this.fireEvent('edit', this, file);
32897                 return;
32898             }
32899
32900             this.uploadStart(file, false);
32901
32902             return;
32903         }
32904         
32905     },
32906     
32907     uploadStart : function(file, crop)
32908     {
32909         this.xhr = new XMLHttpRequest();
32910         
32911         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32912             this.arrange();
32913             return;
32914         }
32915         
32916         file.xhr = this.xhr;
32917             
32918         this.managerEl.createChild({
32919             tag : 'div',
32920             cls : 'roo-document-manager-loading',
32921             cn : [
32922                 {
32923                     tag : 'div',
32924                     tooltip : file.name,
32925                     cls : 'roo-document-manager-thumb',
32926                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32927                 }
32928             ]
32929
32930         });
32931
32932         this.xhr.open(this.method, this.url, true);
32933         
32934         var headers = {
32935             "Accept": "application/json",
32936             "Cache-Control": "no-cache",
32937             "X-Requested-With": "XMLHttpRequest"
32938         };
32939         
32940         for (var headerName in headers) {
32941             var headerValue = headers[headerName];
32942             if (headerValue) {
32943                 this.xhr.setRequestHeader(headerName, headerValue);
32944             }
32945         }
32946         
32947         var _this = this;
32948         
32949         this.xhr.onload = function()
32950         {
32951             _this.xhrOnLoad(_this.xhr);
32952         }
32953         
32954         this.xhr.onerror = function()
32955         {
32956             _this.xhrOnError(_this.xhr);
32957         }
32958         
32959         var formData = new FormData();
32960
32961         formData.append('returnHTML', 'NO');
32962         
32963         if(crop){
32964             formData.append('crop', crop);
32965         }
32966         
32967         formData.append(this.paramName, file, file.name);
32968         
32969         var options = {
32970             file : file, 
32971             manually : false
32972         };
32973         
32974         if(this.fireEvent('prepare', this, formData, options) != false){
32975             
32976             if(options.manually){
32977                 return;
32978             }
32979             
32980             this.xhr.send(formData);
32981             return;
32982         };
32983         
32984         this.uploadCancel();
32985     },
32986     
32987     uploadCancel : function()
32988     {
32989         if (this.xhr) {
32990             this.xhr.abort();
32991         }
32992         
32993         this.delegates = [];
32994         
32995         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32996             el.remove();
32997         }, this);
32998         
32999         this.arrange();
33000     },
33001     
33002     renderPreview : function(file)
33003     {
33004         if(typeof(file.target) != 'undefined' && file.target){
33005             return file;
33006         }
33007         
33008         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33009         
33010         var previewEl = this.managerEl.createChild({
33011             tag : 'div',
33012             cls : 'roo-document-manager-preview',
33013             cn : [
33014                 {
33015                     tag : 'div',
33016                     tooltip : file[this.toolTipName],
33017                     cls : 'roo-document-manager-thumb',
33018                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33019                 },
33020                 {
33021                     tag : 'button',
33022                     cls : 'close',
33023                     html : '<i class="fa fa-times-circle"></i>'
33024                 }
33025             ]
33026         });
33027
33028         var close = previewEl.select('button.close', true).first();
33029
33030         close.on('click', this.onRemove, this, file);
33031
33032         file.target = previewEl;
33033
33034         var image = previewEl.select('img', true).first();
33035         
33036         var _this = this;
33037         
33038         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33039         
33040         image.on('click', this.onClick, this, file);
33041         
33042         this.fireEvent('previewrendered', this, file);
33043         
33044         return file;
33045         
33046     },
33047     
33048     onPreviewLoad : function(file, image)
33049     {
33050         if(typeof(file.target) == 'undefined' || !file.target){
33051             return;
33052         }
33053         
33054         var width = image.dom.naturalWidth || image.dom.width;
33055         var height = image.dom.naturalHeight || image.dom.height;
33056         
33057         if(!this.previewResize) {
33058             return;
33059         }
33060         
33061         if(width > height){
33062             file.target.addClass('wide');
33063             return;
33064         }
33065         
33066         file.target.addClass('tall');
33067         return;
33068         
33069     },
33070     
33071     uploadFromSource : function(file, crop)
33072     {
33073         this.xhr = new XMLHttpRequest();
33074         
33075         this.managerEl.createChild({
33076             tag : 'div',
33077             cls : 'roo-document-manager-loading',
33078             cn : [
33079                 {
33080                     tag : 'div',
33081                     tooltip : file.name,
33082                     cls : 'roo-document-manager-thumb',
33083                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33084                 }
33085             ]
33086
33087         });
33088
33089         this.xhr.open(this.method, this.url, true);
33090         
33091         var headers = {
33092             "Accept": "application/json",
33093             "Cache-Control": "no-cache",
33094             "X-Requested-With": "XMLHttpRequest"
33095         };
33096         
33097         for (var headerName in headers) {
33098             var headerValue = headers[headerName];
33099             if (headerValue) {
33100                 this.xhr.setRequestHeader(headerName, headerValue);
33101             }
33102         }
33103         
33104         var _this = this;
33105         
33106         this.xhr.onload = function()
33107         {
33108             _this.xhrOnLoad(_this.xhr);
33109         }
33110         
33111         this.xhr.onerror = function()
33112         {
33113             _this.xhrOnError(_this.xhr);
33114         }
33115         
33116         var formData = new FormData();
33117
33118         formData.append('returnHTML', 'NO');
33119         
33120         formData.append('crop', crop);
33121         
33122         if(typeof(file.filename) != 'undefined'){
33123             formData.append('filename', file.filename);
33124         }
33125         
33126         if(typeof(file.mimetype) != 'undefined'){
33127             formData.append('mimetype', file.mimetype);
33128         }
33129         
33130         Roo.log(formData);
33131         
33132         if(this.fireEvent('prepare', this, formData) != false){
33133             this.xhr.send(formData);
33134         };
33135     }
33136 });
33137
33138 /*
33139 * Licence: LGPL
33140 */
33141
33142 /**
33143  * @class Roo.bootstrap.DocumentViewer
33144  * @extends Roo.bootstrap.Component
33145  * Bootstrap DocumentViewer class
33146  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33147  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33148  * 
33149  * @constructor
33150  * Create a new DocumentViewer
33151  * @param {Object} config The config object
33152  */
33153
33154 Roo.bootstrap.DocumentViewer = function(config){
33155     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33156     
33157     this.addEvents({
33158         /**
33159          * @event initial
33160          * Fire after initEvent
33161          * @param {Roo.bootstrap.DocumentViewer} this
33162          */
33163         "initial" : true,
33164         /**
33165          * @event click
33166          * Fire after click
33167          * @param {Roo.bootstrap.DocumentViewer} this
33168          */
33169         "click" : true,
33170         /**
33171          * @event download
33172          * Fire after download button
33173          * @param {Roo.bootstrap.DocumentViewer} this
33174          */
33175         "download" : true,
33176         /**
33177          * @event trash
33178          * Fire after trash button
33179          * @param {Roo.bootstrap.DocumentViewer} this
33180          */
33181         "trash" : true
33182         
33183     });
33184 };
33185
33186 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33187     
33188     showDownload : true,
33189     
33190     showTrash : true,
33191     
33192     getAutoCreate : function()
33193     {
33194         var cfg = {
33195             tag : 'div',
33196             cls : 'roo-document-viewer',
33197             cn : [
33198                 {
33199                     tag : 'div',
33200                     cls : 'roo-document-viewer-body',
33201                     cn : [
33202                         {
33203                             tag : 'div',
33204                             cls : 'roo-document-viewer-thumb',
33205                             cn : [
33206                                 {
33207                                     tag : 'img',
33208                                     cls : 'roo-document-viewer-image'
33209                                 }
33210                             ]
33211                         }
33212                     ]
33213                 },
33214                 {
33215                     tag : 'div',
33216                     cls : 'roo-document-viewer-footer',
33217                     cn : {
33218                         tag : 'div',
33219                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33220                         cn : [
33221                             {
33222                                 tag : 'div',
33223                                 cls : 'btn-group roo-document-viewer-download',
33224                                 cn : [
33225                                     {
33226                                         tag : 'button',
33227                                         cls : 'btn btn-default',
33228                                         html : '<i class="fa fa-download"></i>'
33229                                     }
33230                                 ]
33231                             },
33232                             {
33233                                 tag : 'div',
33234                                 cls : 'btn-group roo-document-viewer-trash',
33235                                 cn : [
33236                                     {
33237                                         tag : 'button',
33238                                         cls : 'btn btn-default',
33239                                         html : '<i class="fa fa-trash"></i>'
33240                                     }
33241                                 ]
33242                             }
33243                         ]
33244                     }
33245                 }
33246             ]
33247         };
33248         
33249         return cfg;
33250     },
33251     
33252     initEvents : function()
33253     {
33254         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33255         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33256         
33257         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33258         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33259         
33260         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33261         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33262         
33263         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33264         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33265         
33266         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33267         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33268         
33269         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33270         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33271         
33272         this.bodyEl.on('click', this.onClick, this);
33273         this.downloadBtn.on('click', this.onDownload, this);
33274         this.trashBtn.on('click', this.onTrash, this);
33275         
33276         this.downloadBtn.hide();
33277         this.trashBtn.hide();
33278         
33279         if(this.showDownload){
33280             this.downloadBtn.show();
33281         }
33282         
33283         if(this.showTrash){
33284             this.trashBtn.show();
33285         }
33286         
33287         if(!this.showDownload && !this.showTrash) {
33288             this.footerEl.hide();
33289         }
33290         
33291     },
33292     
33293     initial : function()
33294     {
33295         this.fireEvent('initial', this);
33296         
33297     },
33298     
33299     onClick : function(e)
33300     {
33301         e.preventDefault();
33302         
33303         this.fireEvent('click', this);
33304     },
33305     
33306     onDownload : function(e)
33307     {
33308         e.preventDefault();
33309         
33310         this.fireEvent('download', this);
33311     },
33312     
33313     onTrash : function(e)
33314     {
33315         e.preventDefault();
33316         
33317         this.fireEvent('trash', this);
33318     }
33319     
33320 });
33321 /*
33322  * - LGPL
33323  *
33324  * nav progress bar
33325  * 
33326  */
33327
33328 /**
33329  * @class Roo.bootstrap.NavProgressBar
33330  * @extends Roo.bootstrap.Component
33331  * Bootstrap NavProgressBar class
33332  * 
33333  * @constructor
33334  * Create a new nav progress bar
33335  * @param {Object} config The config object
33336  */
33337
33338 Roo.bootstrap.NavProgressBar = function(config){
33339     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33340
33341     this.bullets = this.bullets || [];
33342    
33343 //    Roo.bootstrap.NavProgressBar.register(this);
33344      this.addEvents({
33345         /**
33346              * @event changed
33347              * Fires when the active item changes
33348              * @param {Roo.bootstrap.NavProgressBar} this
33349              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33350              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33351          */
33352         'changed': true
33353      });
33354     
33355 };
33356
33357 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33358     
33359     bullets : [],
33360     barItems : [],
33361     
33362     getAutoCreate : function()
33363     {
33364         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33365         
33366         cfg = {
33367             tag : 'div',
33368             cls : 'roo-navigation-bar-group',
33369             cn : [
33370                 {
33371                     tag : 'div',
33372                     cls : 'roo-navigation-top-bar'
33373                 },
33374                 {
33375                     tag : 'div',
33376                     cls : 'roo-navigation-bullets-bar',
33377                     cn : [
33378                         {
33379                             tag : 'ul',
33380                             cls : 'roo-navigation-bar'
33381                         }
33382                     ]
33383                 },
33384                 
33385                 {
33386                     tag : 'div',
33387                     cls : 'roo-navigation-bottom-bar'
33388                 }
33389             ]
33390             
33391         };
33392         
33393         return cfg;
33394         
33395     },
33396     
33397     initEvents: function() 
33398     {
33399         
33400     },
33401     
33402     onRender : function(ct, position) 
33403     {
33404         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33405         
33406         if(this.bullets.length){
33407             Roo.each(this.bullets, function(b){
33408                this.addItem(b);
33409             }, this);
33410         }
33411         
33412         this.format();
33413         
33414     },
33415     
33416     addItem : function(cfg)
33417     {
33418         var item = new Roo.bootstrap.NavProgressItem(cfg);
33419         
33420         item.parentId = this.id;
33421         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33422         
33423         if(cfg.html){
33424             var top = new Roo.bootstrap.Element({
33425                 tag : 'div',
33426                 cls : 'roo-navigation-bar-text'
33427             });
33428             
33429             var bottom = new Roo.bootstrap.Element({
33430                 tag : 'div',
33431                 cls : 'roo-navigation-bar-text'
33432             });
33433             
33434             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33435             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33436             
33437             var topText = new Roo.bootstrap.Element({
33438                 tag : 'span',
33439                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33440             });
33441             
33442             var bottomText = new Roo.bootstrap.Element({
33443                 tag : 'span',
33444                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33445             });
33446             
33447             topText.onRender(top.el, null);
33448             bottomText.onRender(bottom.el, null);
33449             
33450             item.topEl = top;
33451             item.bottomEl = bottom;
33452         }
33453         
33454         this.barItems.push(item);
33455         
33456         return item;
33457     },
33458     
33459     getActive : function()
33460     {
33461         var active = false;
33462         
33463         Roo.each(this.barItems, function(v){
33464             
33465             if (!v.isActive()) {
33466                 return;
33467             }
33468             
33469             active = v;
33470             return false;
33471             
33472         });
33473         
33474         return active;
33475     },
33476     
33477     setActiveItem : function(item)
33478     {
33479         var prev = false;
33480         
33481         Roo.each(this.barItems, function(v){
33482             if (v.rid == item.rid) {
33483                 return ;
33484             }
33485             
33486             if (v.isActive()) {
33487                 v.setActive(false);
33488                 prev = v;
33489             }
33490         });
33491
33492         item.setActive(true);
33493         
33494         this.fireEvent('changed', this, item, prev);
33495     },
33496     
33497     getBarItem: function(rid)
33498     {
33499         var ret = false;
33500         
33501         Roo.each(this.barItems, function(e) {
33502             if (e.rid != rid) {
33503                 return;
33504             }
33505             
33506             ret =  e;
33507             return false;
33508         });
33509         
33510         return ret;
33511     },
33512     
33513     indexOfItem : function(item)
33514     {
33515         var index = false;
33516         
33517         Roo.each(this.barItems, function(v, i){
33518             
33519             if (v.rid != item.rid) {
33520                 return;
33521             }
33522             
33523             index = i;
33524             return false
33525         });
33526         
33527         return index;
33528     },
33529     
33530     setActiveNext : function()
33531     {
33532         var i = this.indexOfItem(this.getActive());
33533         
33534         if (i > this.barItems.length) {
33535             return;
33536         }
33537         
33538         this.setActiveItem(this.barItems[i+1]);
33539     },
33540     
33541     setActivePrev : function()
33542     {
33543         var i = this.indexOfItem(this.getActive());
33544         
33545         if (i  < 1) {
33546             return;
33547         }
33548         
33549         this.setActiveItem(this.barItems[i-1]);
33550     },
33551     
33552     format : function()
33553     {
33554         if(!this.barItems.length){
33555             return;
33556         }
33557      
33558         var width = 100 / this.barItems.length;
33559         
33560         Roo.each(this.barItems, function(i){
33561             i.el.setStyle('width', width + '%');
33562             i.topEl.el.setStyle('width', width + '%');
33563             i.bottomEl.el.setStyle('width', width + '%');
33564         }, this);
33565         
33566     }
33567     
33568 });
33569 /*
33570  * - LGPL
33571  *
33572  * Nav Progress Item
33573  * 
33574  */
33575
33576 /**
33577  * @class Roo.bootstrap.NavProgressItem
33578  * @extends Roo.bootstrap.Component
33579  * Bootstrap NavProgressItem class
33580  * @cfg {String} rid the reference id
33581  * @cfg {Boolean} active (true|false) Is item active default false
33582  * @cfg {Boolean} disabled (true|false) Is item active default false
33583  * @cfg {String} html
33584  * @cfg {String} position (top|bottom) text position default bottom
33585  * @cfg {String} icon show icon instead of number
33586  * 
33587  * @constructor
33588  * Create a new NavProgressItem
33589  * @param {Object} config The config object
33590  */
33591 Roo.bootstrap.NavProgressItem = function(config){
33592     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33593     this.addEvents({
33594         // raw events
33595         /**
33596          * @event click
33597          * The raw click event for the entire grid.
33598          * @param {Roo.bootstrap.NavProgressItem} this
33599          * @param {Roo.EventObject} e
33600          */
33601         "click" : true
33602     });
33603    
33604 };
33605
33606 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33607     
33608     rid : '',
33609     active : false,
33610     disabled : false,
33611     html : '',
33612     position : 'bottom',
33613     icon : false,
33614     
33615     getAutoCreate : function()
33616     {
33617         var iconCls = 'roo-navigation-bar-item-icon';
33618         
33619         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33620         
33621         var cfg = {
33622             tag: 'li',
33623             cls: 'roo-navigation-bar-item',
33624             cn : [
33625                 {
33626                     tag : 'i',
33627                     cls : iconCls
33628                 }
33629             ]
33630         };
33631         
33632         if(this.active){
33633             cfg.cls += ' active';
33634         }
33635         if(this.disabled){
33636             cfg.cls += ' disabled';
33637         }
33638         
33639         return cfg;
33640     },
33641     
33642     disable : function()
33643     {
33644         this.setDisabled(true);
33645     },
33646     
33647     enable : function()
33648     {
33649         this.setDisabled(false);
33650     },
33651     
33652     initEvents: function() 
33653     {
33654         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33655         
33656         this.iconEl.on('click', this.onClick, this);
33657     },
33658     
33659     onClick : function(e)
33660     {
33661         e.preventDefault();
33662         
33663         if(this.disabled){
33664             return;
33665         }
33666         
33667         if(this.fireEvent('click', this, e) === false){
33668             return;
33669         };
33670         
33671         this.parent().setActiveItem(this);
33672     },
33673     
33674     isActive: function () 
33675     {
33676         return this.active;
33677     },
33678     
33679     setActive : function(state)
33680     {
33681         if(this.active == state){
33682             return;
33683         }
33684         
33685         this.active = state;
33686         
33687         if (state) {
33688             this.el.addClass('active');
33689             return;
33690         }
33691         
33692         this.el.removeClass('active');
33693         
33694         return;
33695     },
33696     
33697     setDisabled : function(state)
33698     {
33699         if(this.disabled == state){
33700             return;
33701         }
33702         
33703         this.disabled = state;
33704         
33705         if (state) {
33706             this.el.addClass('disabled');
33707             return;
33708         }
33709         
33710         this.el.removeClass('disabled');
33711     },
33712     
33713     tooltipEl : function()
33714     {
33715         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33716     }
33717 });
33718  
33719
33720  /*
33721  * - LGPL
33722  *
33723  * FieldLabel
33724  * 
33725  */
33726
33727 /**
33728  * @class Roo.bootstrap.FieldLabel
33729  * @extends Roo.bootstrap.Component
33730  * Bootstrap FieldLabel class
33731  * @cfg {String} html contents of the element
33732  * @cfg {String} tag tag of the element default label
33733  * @cfg {String} cls class of the element
33734  * @cfg {String} target label target 
33735  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33736  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33737  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33738  * @cfg {String} iconTooltip default "This field is required"
33739  * @cfg {String} indicatorpos (left|right) default left
33740  * 
33741  * @constructor
33742  * Create a new FieldLabel
33743  * @param {Object} config The config object
33744  */
33745
33746 Roo.bootstrap.FieldLabel = function(config){
33747     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33748     
33749     this.addEvents({
33750             /**
33751              * @event invalid
33752              * Fires after the field has been marked as invalid.
33753              * @param {Roo.form.FieldLabel} this
33754              * @param {String} msg The validation message
33755              */
33756             invalid : true,
33757             /**
33758              * @event valid
33759              * Fires after the field has been validated with no errors.
33760              * @param {Roo.form.FieldLabel} this
33761              */
33762             valid : true
33763         });
33764 };
33765
33766 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33767     
33768     tag: 'label',
33769     cls: '',
33770     html: '',
33771     target: '',
33772     allowBlank : true,
33773     invalidClass : 'has-warning',
33774     validClass : 'has-success',
33775     iconTooltip : 'This field is required',
33776     indicatorpos : 'left',
33777     
33778     getAutoCreate : function(){
33779         
33780         var cls = "";
33781         if (!this.allowBlank) {
33782             cls  = "visible";
33783         }
33784         
33785         var cfg = {
33786             tag : this.tag,
33787             cls : 'roo-bootstrap-field-label ' + this.cls,
33788             for : this.target,
33789             cn : [
33790                 {
33791                     tag : 'i',
33792                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33793                     tooltip : this.iconTooltip
33794                 },
33795                 {
33796                     tag : 'span',
33797                     html : this.html
33798                 }
33799             ] 
33800         };
33801         
33802         if(this.indicatorpos == 'right'){
33803             var cfg = {
33804                 tag : this.tag,
33805                 cls : 'roo-bootstrap-field-label ' + this.cls,
33806                 for : this.target,
33807                 cn : [
33808                     {
33809                         tag : 'span',
33810                         html : this.html
33811                     },
33812                     {
33813                         tag : 'i',
33814                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33815                         tooltip : this.iconTooltip
33816                     }
33817                 ] 
33818             };
33819         }
33820         
33821         return cfg;
33822     },
33823     
33824     initEvents: function() 
33825     {
33826         Roo.bootstrap.Element.superclass.initEvents.call(this);
33827         
33828         this.indicator = this.indicatorEl();
33829         
33830         if(this.indicator){
33831             this.indicator.removeClass('visible');
33832             this.indicator.addClass('invisible');
33833         }
33834         
33835         Roo.bootstrap.FieldLabel.register(this);
33836     },
33837     
33838     indicatorEl : function()
33839     {
33840         var indicator = this.el.select('i.roo-required-indicator',true).first();
33841         
33842         if(!indicator){
33843             return false;
33844         }
33845         
33846         return indicator;
33847         
33848     },
33849     
33850     /**
33851      * Mark this field as valid
33852      */
33853     markValid : function()
33854     {
33855         if(this.indicator){
33856             this.indicator.removeClass('visible');
33857             this.indicator.addClass('invisible');
33858         }
33859         if (Roo.bootstrap.version == 3) {
33860             this.el.removeClass(this.invalidClass);
33861             this.el.addClass(this.validClass);
33862         } else {
33863             this.el.removeClass('is-invalid');
33864             this.el.addClass('is-valid');
33865         }
33866         
33867         
33868         this.fireEvent('valid', this);
33869     },
33870     
33871     /**
33872      * Mark this field as invalid
33873      * @param {String} msg The validation message
33874      */
33875     markInvalid : function(msg)
33876     {
33877         if(this.indicator){
33878             this.indicator.removeClass('invisible');
33879             this.indicator.addClass('visible');
33880         }
33881           if (Roo.bootstrap.version == 3) {
33882             this.el.removeClass(this.validClass);
33883             this.el.addClass(this.invalidClass);
33884         } else {
33885             this.el.removeClass('is-valid');
33886             this.el.addClass('is-invalid');
33887         }
33888         
33889         
33890         this.fireEvent('invalid', this, msg);
33891     }
33892     
33893    
33894 });
33895
33896 Roo.apply(Roo.bootstrap.FieldLabel, {
33897     
33898     groups: {},
33899     
33900      /**
33901     * register a FieldLabel Group
33902     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33903     */
33904     register : function(label)
33905     {
33906         if(this.groups.hasOwnProperty(label.target)){
33907             return;
33908         }
33909      
33910         this.groups[label.target] = label;
33911         
33912     },
33913     /**
33914     * fetch a FieldLabel Group based on the target
33915     * @param {string} target
33916     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33917     */
33918     get: function(target) {
33919         if (typeof(this.groups[target]) == 'undefined') {
33920             return false;
33921         }
33922         
33923         return this.groups[target] ;
33924     }
33925 });
33926
33927  
33928
33929  /*
33930  * - LGPL
33931  *
33932  * page DateSplitField.
33933  * 
33934  */
33935
33936
33937 /**
33938  * @class Roo.bootstrap.DateSplitField
33939  * @extends Roo.bootstrap.Component
33940  * Bootstrap DateSplitField class
33941  * @cfg {string} fieldLabel - the label associated
33942  * @cfg {Number} labelWidth set the width of label (0-12)
33943  * @cfg {String} labelAlign (top|left)
33944  * @cfg {Boolean} dayAllowBlank (true|false) default false
33945  * @cfg {Boolean} monthAllowBlank (true|false) default false
33946  * @cfg {Boolean} yearAllowBlank (true|false) default false
33947  * @cfg {string} dayPlaceholder 
33948  * @cfg {string} monthPlaceholder
33949  * @cfg {string} yearPlaceholder
33950  * @cfg {string} dayFormat default 'd'
33951  * @cfg {string} monthFormat default 'm'
33952  * @cfg {string} yearFormat default 'Y'
33953  * @cfg {Number} labellg set the width of label (1-12)
33954  * @cfg {Number} labelmd set the width of label (1-12)
33955  * @cfg {Number} labelsm set the width of label (1-12)
33956  * @cfg {Number} labelxs set the width of label (1-12)
33957
33958  *     
33959  * @constructor
33960  * Create a new DateSplitField
33961  * @param {Object} config The config object
33962  */
33963
33964 Roo.bootstrap.DateSplitField = function(config){
33965     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33966     
33967     this.addEvents({
33968         // raw events
33969          /**
33970          * @event years
33971          * getting the data of years
33972          * @param {Roo.bootstrap.DateSplitField} this
33973          * @param {Object} years
33974          */
33975         "years" : true,
33976         /**
33977          * @event days
33978          * getting the data of days
33979          * @param {Roo.bootstrap.DateSplitField} this
33980          * @param {Object} days
33981          */
33982         "days" : true,
33983         /**
33984          * @event invalid
33985          * Fires after the field has been marked as invalid.
33986          * @param {Roo.form.Field} this
33987          * @param {String} msg The validation message
33988          */
33989         invalid : true,
33990        /**
33991          * @event valid
33992          * Fires after the field has been validated with no errors.
33993          * @param {Roo.form.Field} this
33994          */
33995         valid : true
33996     });
33997 };
33998
33999 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
34000     
34001     fieldLabel : '',
34002     labelAlign : 'top',
34003     labelWidth : 3,
34004     dayAllowBlank : false,
34005     monthAllowBlank : false,
34006     yearAllowBlank : false,
34007     dayPlaceholder : '',
34008     monthPlaceholder : '',
34009     yearPlaceholder : '',
34010     dayFormat : 'd',
34011     monthFormat : 'm',
34012     yearFormat : 'Y',
34013     isFormField : true,
34014     labellg : 0,
34015     labelmd : 0,
34016     labelsm : 0,
34017     labelxs : 0,
34018     
34019     getAutoCreate : function()
34020     {
34021         var cfg = {
34022             tag : 'div',
34023             cls : 'row roo-date-split-field-group',
34024             cn : [
34025                 {
34026                     tag : 'input',
34027                     type : 'hidden',
34028                     cls : 'form-hidden-field roo-date-split-field-group-value',
34029                     name : this.name
34030                 }
34031             ]
34032         };
34033         
34034         var labelCls = 'col-md-12';
34035         var contentCls = 'col-md-4';
34036         
34037         if(this.fieldLabel){
34038             
34039             var label = {
34040                 tag : 'div',
34041                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34042                 cn : [
34043                     {
34044                         tag : 'label',
34045                         html : this.fieldLabel
34046                     }
34047                 ]
34048             };
34049             
34050             if(this.labelAlign == 'left'){
34051             
34052                 if(this.labelWidth > 12){
34053                     label.style = "width: " + this.labelWidth + 'px';
34054                 }
34055
34056                 if(this.labelWidth < 13 && this.labelmd == 0){
34057                     this.labelmd = this.labelWidth;
34058                 }
34059
34060                 if(this.labellg > 0){
34061                     labelCls = ' col-lg-' + this.labellg;
34062                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34063                 }
34064
34065                 if(this.labelmd > 0){
34066                     labelCls = ' col-md-' + this.labelmd;
34067                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34068                 }
34069
34070                 if(this.labelsm > 0){
34071                     labelCls = ' col-sm-' + this.labelsm;
34072                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34073                 }
34074
34075                 if(this.labelxs > 0){
34076                     labelCls = ' col-xs-' + this.labelxs;
34077                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34078                 }
34079             }
34080             
34081             label.cls += ' ' + labelCls;
34082             
34083             cfg.cn.push(label);
34084         }
34085         
34086         Roo.each(['day', 'month', 'year'], function(t){
34087             cfg.cn.push({
34088                 tag : 'div',
34089                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34090             });
34091         }, this);
34092         
34093         return cfg;
34094     },
34095     
34096     inputEl: function ()
34097     {
34098         return this.el.select('.roo-date-split-field-group-value', true).first();
34099     },
34100     
34101     onRender : function(ct, position) 
34102     {
34103         var _this = this;
34104         
34105         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34106         
34107         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34108         
34109         this.dayField = new Roo.bootstrap.ComboBox({
34110             allowBlank : this.dayAllowBlank,
34111             alwaysQuery : true,
34112             displayField : 'value',
34113             editable : false,
34114             fieldLabel : '',
34115             forceSelection : true,
34116             mode : 'local',
34117             placeholder : this.dayPlaceholder,
34118             selectOnFocus : true,
34119             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34120             triggerAction : 'all',
34121             typeAhead : true,
34122             valueField : 'value',
34123             store : new Roo.data.SimpleStore({
34124                 data : (function() {    
34125                     var days = [];
34126                     _this.fireEvent('days', _this, days);
34127                     return days;
34128                 })(),
34129                 fields : [ 'value' ]
34130             }),
34131             listeners : {
34132                 select : function (_self, record, index)
34133                 {
34134                     _this.setValue(_this.getValue());
34135                 }
34136             }
34137         });
34138
34139         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34140         
34141         this.monthField = new Roo.bootstrap.MonthField({
34142             after : '<i class=\"fa fa-calendar\"></i>',
34143             allowBlank : this.monthAllowBlank,
34144             placeholder : this.monthPlaceholder,
34145             readOnly : true,
34146             listeners : {
34147                 render : function (_self)
34148                 {
34149                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34150                         e.preventDefault();
34151                         _self.focus();
34152                     });
34153                 },
34154                 select : function (_self, oldvalue, newvalue)
34155                 {
34156                     _this.setValue(_this.getValue());
34157                 }
34158             }
34159         });
34160         
34161         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34162         
34163         this.yearField = new Roo.bootstrap.ComboBox({
34164             allowBlank : this.yearAllowBlank,
34165             alwaysQuery : true,
34166             displayField : 'value',
34167             editable : false,
34168             fieldLabel : '',
34169             forceSelection : true,
34170             mode : 'local',
34171             placeholder : this.yearPlaceholder,
34172             selectOnFocus : true,
34173             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34174             triggerAction : 'all',
34175             typeAhead : true,
34176             valueField : 'value',
34177             store : new Roo.data.SimpleStore({
34178                 data : (function() {
34179                     var years = [];
34180                     _this.fireEvent('years', _this, years);
34181                     return years;
34182                 })(),
34183                 fields : [ 'value' ]
34184             }),
34185             listeners : {
34186                 select : function (_self, record, index)
34187                 {
34188                     _this.setValue(_this.getValue());
34189                 }
34190             }
34191         });
34192
34193         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34194     },
34195     
34196     setValue : function(v, format)
34197     {
34198         this.inputEl.dom.value = v;
34199         
34200         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34201         
34202         var d = Date.parseDate(v, f);
34203         
34204         if(!d){
34205             this.validate();
34206             return;
34207         }
34208         
34209         this.setDay(d.format(this.dayFormat));
34210         this.setMonth(d.format(this.monthFormat));
34211         this.setYear(d.format(this.yearFormat));
34212         
34213         this.validate();
34214         
34215         return;
34216     },
34217     
34218     setDay : function(v)
34219     {
34220         this.dayField.setValue(v);
34221         this.inputEl.dom.value = this.getValue();
34222         this.validate();
34223         return;
34224     },
34225     
34226     setMonth : function(v)
34227     {
34228         this.monthField.setValue(v, true);
34229         this.inputEl.dom.value = this.getValue();
34230         this.validate();
34231         return;
34232     },
34233     
34234     setYear : function(v)
34235     {
34236         this.yearField.setValue(v);
34237         this.inputEl.dom.value = this.getValue();
34238         this.validate();
34239         return;
34240     },
34241     
34242     getDay : function()
34243     {
34244         return this.dayField.getValue();
34245     },
34246     
34247     getMonth : function()
34248     {
34249         return this.monthField.getValue();
34250     },
34251     
34252     getYear : function()
34253     {
34254         return this.yearField.getValue();
34255     },
34256     
34257     getValue : function()
34258     {
34259         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34260         
34261         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34262         
34263         return date;
34264     },
34265     
34266     reset : function()
34267     {
34268         this.setDay('');
34269         this.setMonth('');
34270         this.setYear('');
34271         this.inputEl.dom.value = '';
34272         this.validate();
34273         return;
34274     },
34275     
34276     validate : function()
34277     {
34278         var d = this.dayField.validate();
34279         var m = this.monthField.validate();
34280         var y = this.yearField.validate();
34281         
34282         var valid = true;
34283         
34284         if(
34285                 (!this.dayAllowBlank && !d) ||
34286                 (!this.monthAllowBlank && !m) ||
34287                 (!this.yearAllowBlank && !y)
34288         ){
34289             valid = false;
34290         }
34291         
34292         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34293             return valid;
34294         }
34295         
34296         if(valid){
34297             this.markValid();
34298             return valid;
34299         }
34300         
34301         this.markInvalid();
34302         
34303         return valid;
34304     },
34305     
34306     markValid : function()
34307     {
34308         
34309         var label = this.el.select('label', true).first();
34310         var icon = this.el.select('i.fa-star', true).first();
34311
34312         if(label && icon){
34313             icon.remove();
34314         }
34315         
34316         this.fireEvent('valid', this);
34317     },
34318     
34319      /**
34320      * Mark this field as invalid
34321      * @param {String} msg The validation message
34322      */
34323     markInvalid : function(msg)
34324     {
34325         
34326         var label = this.el.select('label', true).first();
34327         var icon = this.el.select('i.fa-star', true).first();
34328
34329         if(label && !icon){
34330             this.el.select('.roo-date-split-field-label', true).createChild({
34331                 tag : 'i',
34332                 cls : 'text-danger fa fa-lg fa-star',
34333                 tooltip : 'This field is required',
34334                 style : 'margin-right:5px;'
34335             }, label, true);
34336         }
34337         
34338         this.fireEvent('invalid', this, msg);
34339     },
34340     
34341     clearInvalid : function()
34342     {
34343         var label = this.el.select('label', true).first();
34344         var icon = this.el.select('i.fa-star', true).first();
34345
34346         if(label && icon){
34347             icon.remove();
34348         }
34349         
34350         this.fireEvent('valid', this);
34351     },
34352     
34353     getName: function()
34354     {
34355         return this.name;
34356     }
34357     
34358 });
34359
34360  /**
34361  *
34362  * This is based on 
34363  * http://masonry.desandro.com
34364  *
34365  * The idea is to render all the bricks based on vertical width...
34366  *
34367  * The original code extends 'outlayer' - we might need to use that....
34368  * 
34369  */
34370
34371
34372 /**
34373  * @class Roo.bootstrap.LayoutMasonry
34374  * @extends Roo.bootstrap.Component
34375  * Bootstrap Layout Masonry class
34376  * 
34377  * @constructor
34378  * Create a new Element
34379  * @param {Object} config The config object
34380  */
34381
34382 Roo.bootstrap.LayoutMasonry = function(config){
34383     
34384     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34385     
34386     this.bricks = [];
34387     
34388     Roo.bootstrap.LayoutMasonry.register(this);
34389     
34390     this.addEvents({
34391         // raw events
34392         /**
34393          * @event layout
34394          * Fire after layout the items
34395          * @param {Roo.bootstrap.LayoutMasonry} this
34396          * @param {Roo.EventObject} e
34397          */
34398         "layout" : true
34399     });
34400     
34401 };
34402
34403 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34404     
34405     /**
34406      * @cfg {Boolean} isLayoutInstant = no animation?
34407      */   
34408     isLayoutInstant : false, // needed?
34409    
34410     /**
34411      * @cfg {Number} boxWidth  width of the columns
34412      */   
34413     boxWidth : 450,
34414     
34415       /**
34416      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34417      */   
34418     boxHeight : 0,
34419     
34420     /**
34421      * @cfg {Number} padWidth padding below box..
34422      */   
34423     padWidth : 10, 
34424     
34425     /**
34426      * @cfg {Number} gutter gutter width..
34427      */   
34428     gutter : 10,
34429     
34430      /**
34431      * @cfg {Number} maxCols maximum number of columns
34432      */   
34433     
34434     maxCols: 0,
34435     
34436     /**
34437      * @cfg {Boolean} isAutoInitial defalut true
34438      */   
34439     isAutoInitial : true, 
34440     
34441     containerWidth: 0,
34442     
34443     /**
34444      * @cfg {Boolean} isHorizontal defalut false
34445      */   
34446     isHorizontal : false, 
34447
34448     currentSize : null,
34449     
34450     tag: 'div',
34451     
34452     cls: '',
34453     
34454     bricks: null, //CompositeElement
34455     
34456     cols : 1,
34457     
34458     _isLayoutInited : false,
34459     
34460 //    isAlternative : false, // only use for vertical layout...
34461     
34462     /**
34463      * @cfg {Number} alternativePadWidth padding below box..
34464      */   
34465     alternativePadWidth : 50,
34466     
34467     selectedBrick : [],
34468     
34469     getAutoCreate : function(){
34470         
34471         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34472         
34473         var cfg = {
34474             tag: this.tag,
34475             cls: 'blog-masonary-wrapper ' + this.cls,
34476             cn : {
34477                 cls : 'mas-boxes masonary'
34478             }
34479         };
34480         
34481         return cfg;
34482     },
34483     
34484     getChildContainer: function( )
34485     {
34486         if (this.boxesEl) {
34487             return this.boxesEl;
34488         }
34489         
34490         this.boxesEl = this.el.select('.mas-boxes').first();
34491         
34492         return this.boxesEl;
34493     },
34494     
34495     
34496     initEvents : function()
34497     {
34498         var _this = this;
34499         
34500         if(this.isAutoInitial){
34501             Roo.log('hook children rendered');
34502             this.on('childrenrendered', function() {
34503                 Roo.log('children rendered');
34504                 _this.initial();
34505             } ,this);
34506         }
34507     },
34508     
34509     initial : function()
34510     {
34511         this.selectedBrick = [];
34512         
34513         this.currentSize = this.el.getBox(true);
34514         
34515         Roo.EventManager.onWindowResize(this.resize, this); 
34516
34517         if(!this.isAutoInitial){
34518             this.layout();
34519             return;
34520         }
34521         
34522         this.layout();
34523         
34524         return;
34525         //this.layout.defer(500,this);
34526         
34527     },
34528     
34529     resize : function()
34530     {
34531         var cs = this.el.getBox(true);
34532         
34533         if (
34534                 this.currentSize.width == cs.width && 
34535                 this.currentSize.x == cs.x && 
34536                 this.currentSize.height == cs.height && 
34537                 this.currentSize.y == cs.y 
34538         ) {
34539             Roo.log("no change in with or X or Y");
34540             return;
34541         }
34542         
34543         this.currentSize = cs;
34544         
34545         this.layout();
34546         
34547     },
34548     
34549     layout : function()
34550     {   
34551         this._resetLayout();
34552         
34553         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34554         
34555         this.layoutItems( isInstant );
34556       
34557         this._isLayoutInited = true;
34558         
34559         this.fireEvent('layout', this);
34560         
34561     },
34562     
34563     _resetLayout : function()
34564     {
34565         if(this.isHorizontal){
34566             this.horizontalMeasureColumns();
34567             return;
34568         }
34569         
34570         this.verticalMeasureColumns();
34571         
34572     },
34573     
34574     verticalMeasureColumns : function()
34575     {
34576         this.getContainerWidth();
34577         
34578 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34579 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34580 //            return;
34581 //        }
34582         
34583         var boxWidth = this.boxWidth + this.padWidth;
34584         
34585         if(this.containerWidth < this.boxWidth){
34586             boxWidth = this.containerWidth
34587         }
34588         
34589         var containerWidth = this.containerWidth;
34590         
34591         var cols = Math.floor(containerWidth / boxWidth);
34592         
34593         this.cols = Math.max( cols, 1 );
34594         
34595         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34596         
34597         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34598         
34599         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34600         
34601         this.colWidth = boxWidth + avail - this.padWidth;
34602         
34603         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34604         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34605     },
34606     
34607     horizontalMeasureColumns : function()
34608     {
34609         this.getContainerWidth();
34610         
34611         var boxWidth = this.boxWidth;
34612         
34613         if(this.containerWidth < boxWidth){
34614             boxWidth = this.containerWidth;
34615         }
34616         
34617         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34618         
34619         this.el.setHeight(boxWidth);
34620         
34621     },
34622     
34623     getContainerWidth : function()
34624     {
34625         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34626     },
34627     
34628     layoutItems : function( isInstant )
34629     {
34630         Roo.log(this.bricks);
34631         
34632         var items = Roo.apply([], this.bricks);
34633         
34634         if(this.isHorizontal){
34635             this._horizontalLayoutItems( items , isInstant );
34636             return;
34637         }
34638         
34639 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34640 //            this._verticalAlternativeLayoutItems( items , isInstant );
34641 //            return;
34642 //        }
34643         
34644         this._verticalLayoutItems( items , isInstant );
34645         
34646     },
34647     
34648     _verticalLayoutItems : function ( items , isInstant)
34649     {
34650         if ( !items || !items.length ) {
34651             return;
34652         }
34653         
34654         var standard = [
34655             ['xs', 'xs', 'xs', 'tall'],
34656             ['xs', 'xs', 'tall'],
34657             ['xs', 'xs', 'sm'],
34658             ['xs', 'xs', 'xs'],
34659             ['xs', 'tall'],
34660             ['xs', 'sm'],
34661             ['xs', 'xs'],
34662             ['xs'],
34663             
34664             ['sm', 'xs', 'xs'],
34665             ['sm', 'xs'],
34666             ['sm'],
34667             
34668             ['tall', 'xs', 'xs', 'xs'],
34669             ['tall', 'xs', 'xs'],
34670             ['tall', 'xs'],
34671             ['tall']
34672             
34673         ];
34674         
34675         var queue = [];
34676         
34677         var boxes = [];
34678         
34679         var box = [];
34680         
34681         Roo.each(items, function(item, k){
34682             
34683             switch (item.size) {
34684                 // these layouts take up a full box,
34685                 case 'md' :
34686                 case 'md-left' :
34687                 case 'md-right' :
34688                 case 'wide' :
34689                     
34690                     if(box.length){
34691                         boxes.push(box);
34692                         box = [];
34693                     }
34694                     
34695                     boxes.push([item]);
34696                     
34697                     break;
34698                     
34699                 case 'xs' :
34700                 case 'sm' :
34701                 case 'tall' :
34702                     
34703                     box.push(item);
34704                     
34705                     break;
34706                 default :
34707                     break;
34708                     
34709             }
34710             
34711         }, this);
34712         
34713         if(box.length){
34714             boxes.push(box);
34715             box = [];
34716         }
34717         
34718         var filterPattern = function(box, length)
34719         {
34720             if(!box.length){
34721                 return;
34722             }
34723             
34724             var match = false;
34725             
34726             var pattern = box.slice(0, length);
34727             
34728             var format = [];
34729             
34730             Roo.each(pattern, function(i){
34731                 format.push(i.size);
34732             }, this);
34733             
34734             Roo.each(standard, function(s){
34735                 
34736                 if(String(s) != String(format)){
34737                     return;
34738                 }
34739                 
34740                 match = true;
34741                 return false;
34742                 
34743             }, this);
34744             
34745             if(!match && length == 1){
34746                 return;
34747             }
34748             
34749             if(!match){
34750                 filterPattern(box, length - 1);
34751                 return;
34752             }
34753                 
34754             queue.push(pattern);
34755
34756             box = box.slice(length, box.length);
34757
34758             filterPattern(box, 4);
34759
34760             return;
34761             
34762         }
34763         
34764         Roo.each(boxes, function(box, k){
34765             
34766             if(!box.length){
34767                 return;
34768             }
34769             
34770             if(box.length == 1){
34771                 queue.push(box);
34772                 return;
34773             }
34774             
34775             filterPattern(box, 4);
34776             
34777         }, this);
34778         
34779         this._processVerticalLayoutQueue( queue, isInstant );
34780         
34781     },
34782     
34783 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34784 //    {
34785 //        if ( !items || !items.length ) {
34786 //            return;
34787 //        }
34788 //
34789 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34790 //        
34791 //    },
34792     
34793     _horizontalLayoutItems : function ( items , isInstant)
34794     {
34795         if ( !items || !items.length || items.length < 3) {
34796             return;
34797         }
34798         
34799         items.reverse();
34800         
34801         var eItems = items.slice(0, 3);
34802         
34803         items = items.slice(3, items.length);
34804         
34805         var standard = [
34806             ['xs', 'xs', 'xs', 'wide'],
34807             ['xs', 'xs', 'wide'],
34808             ['xs', 'xs', 'sm'],
34809             ['xs', 'xs', 'xs'],
34810             ['xs', 'wide'],
34811             ['xs', 'sm'],
34812             ['xs', 'xs'],
34813             ['xs'],
34814             
34815             ['sm', 'xs', 'xs'],
34816             ['sm', 'xs'],
34817             ['sm'],
34818             
34819             ['wide', 'xs', 'xs', 'xs'],
34820             ['wide', 'xs', 'xs'],
34821             ['wide', 'xs'],
34822             ['wide'],
34823             
34824             ['wide-thin']
34825         ];
34826         
34827         var queue = [];
34828         
34829         var boxes = [];
34830         
34831         var box = [];
34832         
34833         Roo.each(items, function(item, k){
34834             
34835             switch (item.size) {
34836                 case 'md' :
34837                 case 'md-left' :
34838                 case 'md-right' :
34839                 case 'tall' :
34840                     
34841                     if(box.length){
34842                         boxes.push(box);
34843                         box = [];
34844                     }
34845                     
34846                     boxes.push([item]);
34847                     
34848                     break;
34849                     
34850                 case 'xs' :
34851                 case 'sm' :
34852                 case 'wide' :
34853                 case 'wide-thin' :
34854                     
34855                     box.push(item);
34856                     
34857                     break;
34858                 default :
34859                     break;
34860                     
34861             }
34862             
34863         }, this);
34864         
34865         if(box.length){
34866             boxes.push(box);
34867             box = [];
34868         }
34869         
34870         var filterPattern = function(box, length)
34871         {
34872             if(!box.length){
34873                 return;
34874             }
34875             
34876             var match = false;
34877             
34878             var pattern = box.slice(0, length);
34879             
34880             var format = [];
34881             
34882             Roo.each(pattern, function(i){
34883                 format.push(i.size);
34884             }, this);
34885             
34886             Roo.each(standard, function(s){
34887                 
34888                 if(String(s) != String(format)){
34889                     return;
34890                 }
34891                 
34892                 match = true;
34893                 return false;
34894                 
34895             }, this);
34896             
34897             if(!match && length == 1){
34898                 return;
34899             }
34900             
34901             if(!match){
34902                 filterPattern(box, length - 1);
34903                 return;
34904             }
34905                 
34906             queue.push(pattern);
34907
34908             box = box.slice(length, box.length);
34909
34910             filterPattern(box, 4);
34911
34912             return;
34913             
34914         }
34915         
34916         Roo.each(boxes, function(box, k){
34917             
34918             if(!box.length){
34919                 return;
34920             }
34921             
34922             if(box.length == 1){
34923                 queue.push(box);
34924                 return;
34925             }
34926             
34927             filterPattern(box, 4);
34928             
34929         }, this);
34930         
34931         
34932         var prune = [];
34933         
34934         var pos = this.el.getBox(true);
34935         
34936         var minX = pos.x;
34937         
34938         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34939         
34940         var hit_end = false;
34941         
34942         Roo.each(queue, function(box){
34943             
34944             if(hit_end){
34945                 
34946                 Roo.each(box, function(b){
34947                 
34948                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34949                     b.el.hide();
34950
34951                 }, this);
34952
34953                 return;
34954             }
34955             
34956             var mx = 0;
34957             
34958             Roo.each(box, function(b){
34959                 
34960                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34961                 b.el.show();
34962
34963                 mx = Math.max(mx, b.x);
34964                 
34965             }, this);
34966             
34967             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34968             
34969             if(maxX < minX){
34970                 
34971                 Roo.each(box, function(b){
34972                 
34973                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34974                     b.el.hide();
34975                     
34976                 }, this);
34977                 
34978                 hit_end = true;
34979                 
34980                 return;
34981             }
34982             
34983             prune.push(box);
34984             
34985         }, this);
34986         
34987         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34988     },
34989     
34990     /** Sets position of item in DOM
34991     * @param {Element} item
34992     * @param {Number} x - horizontal position
34993     * @param {Number} y - vertical position
34994     * @param {Boolean} isInstant - disables transitions
34995     */
34996     _processVerticalLayoutQueue : function( queue, isInstant )
34997     {
34998         var pos = this.el.getBox(true);
34999         var x = pos.x;
35000         var y = pos.y;
35001         var maxY = [];
35002         
35003         for (var i = 0; i < this.cols; i++){
35004             maxY[i] = pos.y;
35005         }
35006         
35007         Roo.each(queue, function(box, k){
35008             
35009             var col = k % this.cols;
35010             
35011             Roo.each(box, function(b,kk){
35012                 
35013                 b.el.position('absolute');
35014                 
35015                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35016                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35017                 
35018                 if(b.size == 'md-left' || b.size == 'md-right'){
35019                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35020                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35021                 }
35022                 
35023                 b.el.setWidth(width);
35024                 b.el.setHeight(height);
35025                 // iframe?
35026                 b.el.select('iframe',true).setSize(width,height);
35027                 
35028             }, this);
35029             
35030             for (var i = 0; i < this.cols; i++){
35031                 
35032                 if(maxY[i] < maxY[col]){
35033                     col = i;
35034                     continue;
35035                 }
35036                 
35037                 col = Math.min(col, i);
35038                 
35039             }
35040             
35041             x = pos.x + col * (this.colWidth + this.padWidth);
35042             
35043             y = maxY[col];
35044             
35045             var positions = [];
35046             
35047             switch (box.length){
35048                 case 1 :
35049                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35050                     break;
35051                 case 2 :
35052                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35053                     break;
35054                 case 3 :
35055                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35056                     break;
35057                 case 4 :
35058                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35059                     break;
35060                 default :
35061                     break;
35062             }
35063             
35064             Roo.each(box, function(b,kk){
35065                 
35066                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35067                 
35068                 var sz = b.el.getSize();
35069                 
35070                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35071                 
35072             }, this);
35073             
35074         }, this);
35075         
35076         var mY = 0;
35077         
35078         for (var i = 0; i < this.cols; i++){
35079             mY = Math.max(mY, maxY[i]);
35080         }
35081         
35082         this.el.setHeight(mY - pos.y);
35083         
35084     },
35085     
35086 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35087 //    {
35088 //        var pos = this.el.getBox(true);
35089 //        var x = pos.x;
35090 //        var y = pos.y;
35091 //        var maxX = pos.right;
35092 //        
35093 //        var maxHeight = 0;
35094 //        
35095 //        Roo.each(items, function(item, k){
35096 //            
35097 //            var c = k % 2;
35098 //            
35099 //            item.el.position('absolute');
35100 //                
35101 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35102 //
35103 //            item.el.setWidth(width);
35104 //
35105 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35106 //
35107 //            item.el.setHeight(height);
35108 //            
35109 //            if(c == 0){
35110 //                item.el.setXY([x, y], isInstant ? false : true);
35111 //            } else {
35112 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35113 //            }
35114 //            
35115 //            y = y + height + this.alternativePadWidth;
35116 //            
35117 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35118 //            
35119 //        }, this);
35120 //        
35121 //        this.el.setHeight(maxHeight);
35122 //        
35123 //    },
35124     
35125     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35126     {
35127         var pos = this.el.getBox(true);
35128         
35129         var minX = pos.x;
35130         var minY = pos.y;
35131         
35132         var maxX = pos.right;
35133         
35134         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35135         
35136         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35137         
35138         Roo.each(queue, function(box, k){
35139             
35140             Roo.each(box, function(b, kk){
35141                 
35142                 b.el.position('absolute');
35143                 
35144                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35145                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35146                 
35147                 if(b.size == 'md-left' || b.size == 'md-right'){
35148                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35149                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35150                 }
35151                 
35152                 b.el.setWidth(width);
35153                 b.el.setHeight(height);
35154                 
35155             }, this);
35156             
35157             if(!box.length){
35158                 return;
35159             }
35160             
35161             var positions = [];
35162             
35163             switch (box.length){
35164                 case 1 :
35165                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35166                     break;
35167                 case 2 :
35168                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35169                     break;
35170                 case 3 :
35171                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35172                     break;
35173                 case 4 :
35174                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35175                     break;
35176                 default :
35177                     break;
35178             }
35179             
35180             Roo.each(box, function(b,kk){
35181                 
35182                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35183                 
35184                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35185                 
35186             }, this);
35187             
35188         }, this);
35189         
35190     },
35191     
35192     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35193     {
35194         Roo.each(eItems, function(b,k){
35195             
35196             b.size = (k == 0) ? 'sm' : 'xs';
35197             b.x = (k == 0) ? 2 : 1;
35198             b.y = (k == 0) ? 2 : 1;
35199             
35200             b.el.position('absolute');
35201             
35202             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35203                 
35204             b.el.setWidth(width);
35205             
35206             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35207             
35208             b.el.setHeight(height);
35209             
35210         }, this);
35211
35212         var positions = [];
35213         
35214         positions.push({
35215             x : maxX - this.unitWidth * 2 - this.gutter,
35216             y : minY
35217         });
35218         
35219         positions.push({
35220             x : maxX - this.unitWidth,
35221             y : minY + (this.unitWidth + this.gutter) * 2
35222         });
35223         
35224         positions.push({
35225             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35226             y : minY
35227         });
35228         
35229         Roo.each(eItems, function(b,k){
35230             
35231             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35232
35233         }, this);
35234         
35235     },
35236     
35237     getVerticalOneBoxColPositions : function(x, y, box)
35238     {
35239         var pos = [];
35240         
35241         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35242         
35243         if(box[0].size == 'md-left'){
35244             rand = 0;
35245         }
35246         
35247         if(box[0].size == 'md-right'){
35248             rand = 1;
35249         }
35250         
35251         pos.push({
35252             x : x + (this.unitWidth + this.gutter) * rand,
35253             y : y
35254         });
35255         
35256         return pos;
35257     },
35258     
35259     getVerticalTwoBoxColPositions : function(x, y, box)
35260     {
35261         var pos = [];
35262         
35263         if(box[0].size == 'xs'){
35264             
35265             pos.push({
35266                 x : x,
35267                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35268             });
35269
35270             pos.push({
35271                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35272                 y : y
35273             });
35274             
35275             return pos;
35276             
35277         }
35278         
35279         pos.push({
35280             x : x,
35281             y : y
35282         });
35283
35284         pos.push({
35285             x : x + (this.unitWidth + this.gutter) * 2,
35286             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35287         });
35288         
35289         return pos;
35290         
35291     },
35292     
35293     getVerticalThreeBoxColPositions : function(x, y, box)
35294     {
35295         var pos = [];
35296         
35297         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35298             
35299             pos.push({
35300                 x : x,
35301                 y : y
35302             });
35303
35304             pos.push({
35305                 x : x + (this.unitWidth + this.gutter) * 1,
35306                 y : y
35307             });
35308             
35309             pos.push({
35310                 x : x + (this.unitWidth + this.gutter) * 2,
35311                 y : y
35312             });
35313             
35314             return pos;
35315             
35316         }
35317         
35318         if(box[0].size == 'xs' && box[1].size == 'xs'){
35319             
35320             pos.push({
35321                 x : x,
35322                 y : y
35323             });
35324
35325             pos.push({
35326                 x : x,
35327                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35328             });
35329             
35330             pos.push({
35331                 x : x + (this.unitWidth + this.gutter) * 1,
35332                 y : y
35333             });
35334             
35335             return pos;
35336             
35337         }
35338         
35339         pos.push({
35340             x : x,
35341             y : y
35342         });
35343
35344         pos.push({
35345             x : x + (this.unitWidth + this.gutter) * 2,
35346             y : y
35347         });
35348
35349         pos.push({
35350             x : x + (this.unitWidth + this.gutter) * 2,
35351             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35352         });
35353             
35354         return pos;
35355         
35356     },
35357     
35358     getVerticalFourBoxColPositions : function(x, y, box)
35359     {
35360         var pos = [];
35361         
35362         if(box[0].size == 'xs'){
35363             
35364             pos.push({
35365                 x : x,
35366                 y : y
35367             });
35368
35369             pos.push({
35370                 x : x,
35371                 y : y + (this.unitHeight + this.gutter) * 1
35372             });
35373             
35374             pos.push({
35375                 x : x,
35376                 y : y + (this.unitHeight + this.gutter) * 2
35377             });
35378             
35379             pos.push({
35380                 x : x + (this.unitWidth + this.gutter) * 1,
35381                 y : y
35382             });
35383             
35384             return pos;
35385             
35386         }
35387         
35388         pos.push({
35389             x : x,
35390             y : y
35391         });
35392
35393         pos.push({
35394             x : x + (this.unitWidth + this.gutter) * 2,
35395             y : y
35396         });
35397
35398         pos.push({
35399             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35400             y : y + (this.unitHeight + this.gutter) * 1
35401         });
35402
35403         pos.push({
35404             x : x + (this.unitWidth + this.gutter) * 2,
35405             y : y + (this.unitWidth + this.gutter) * 2
35406         });
35407
35408         return pos;
35409         
35410     },
35411     
35412     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35413     {
35414         var pos = [];
35415         
35416         if(box[0].size == 'md-left'){
35417             pos.push({
35418                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35419                 y : minY
35420             });
35421             
35422             return pos;
35423         }
35424         
35425         if(box[0].size == 'md-right'){
35426             pos.push({
35427                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35428                 y : minY + (this.unitWidth + this.gutter) * 1
35429             });
35430             
35431             return pos;
35432         }
35433         
35434         var rand = Math.floor(Math.random() * (4 - box[0].y));
35435         
35436         pos.push({
35437             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35438             y : minY + (this.unitWidth + this.gutter) * rand
35439         });
35440         
35441         return pos;
35442         
35443     },
35444     
35445     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35446     {
35447         var pos = [];
35448         
35449         if(box[0].size == 'xs'){
35450             
35451             pos.push({
35452                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35453                 y : minY
35454             });
35455
35456             pos.push({
35457                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35458                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35459             });
35460             
35461             return pos;
35462             
35463         }
35464         
35465         pos.push({
35466             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35467             y : minY
35468         });
35469
35470         pos.push({
35471             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35472             y : minY + (this.unitWidth + this.gutter) * 2
35473         });
35474         
35475         return pos;
35476         
35477     },
35478     
35479     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35480     {
35481         var pos = [];
35482         
35483         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35484             
35485             pos.push({
35486                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35487                 y : minY
35488             });
35489
35490             pos.push({
35491                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35492                 y : minY + (this.unitWidth + this.gutter) * 1
35493             });
35494             
35495             pos.push({
35496                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35497                 y : minY + (this.unitWidth + this.gutter) * 2
35498             });
35499             
35500             return pos;
35501             
35502         }
35503         
35504         if(box[0].size == 'xs' && box[1].size == 'xs'){
35505             
35506             pos.push({
35507                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35508                 y : minY
35509             });
35510
35511             pos.push({
35512                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35513                 y : minY
35514             });
35515             
35516             pos.push({
35517                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35518                 y : minY + (this.unitWidth + this.gutter) * 1
35519             });
35520             
35521             return pos;
35522             
35523         }
35524         
35525         pos.push({
35526             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35527             y : minY
35528         });
35529
35530         pos.push({
35531             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35532             y : minY + (this.unitWidth + this.gutter) * 2
35533         });
35534
35535         pos.push({
35536             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35537             y : minY + (this.unitWidth + this.gutter) * 2
35538         });
35539             
35540         return pos;
35541         
35542     },
35543     
35544     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35545     {
35546         var pos = [];
35547         
35548         if(box[0].size == 'xs'){
35549             
35550             pos.push({
35551                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35552                 y : minY
35553             });
35554
35555             pos.push({
35556                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35562                 y : minY
35563             });
35564             
35565             pos.push({
35566                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35567                 y : minY + (this.unitWidth + this.gutter) * 1
35568             });
35569             
35570             return pos;
35571             
35572         }
35573         
35574         pos.push({
35575             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35576             y : minY
35577         });
35578         
35579         pos.push({
35580             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35581             y : minY + (this.unitWidth + this.gutter) * 2
35582         });
35583         
35584         pos.push({
35585             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35591             y : minY + (this.unitWidth + this.gutter) * 2
35592         });
35593
35594         return pos;
35595         
35596     },
35597     
35598     /**
35599     * remove a Masonry Brick
35600     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35601     */
35602     removeBrick : function(brick_id)
35603     {
35604         if (!brick_id) {
35605             return;
35606         }
35607         
35608         for (var i = 0; i<this.bricks.length; i++) {
35609             if (this.bricks[i].id == brick_id) {
35610                 this.bricks.splice(i,1);
35611                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35612                 this.initial();
35613             }
35614         }
35615     },
35616     
35617     /**
35618     * adds a Masonry Brick
35619     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35620     */
35621     addBrick : function(cfg)
35622     {
35623         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35624         //this.register(cn);
35625         cn.parentId = this.id;
35626         cn.render(this.el);
35627         return cn;
35628     },
35629     
35630     /**
35631     * register a Masonry Brick
35632     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35633     */
35634     
35635     register : function(brick)
35636     {
35637         this.bricks.push(brick);
35638         brick.masonryId = this.id;
35639     },
35640     
35641     /**
35642     * clear all the Masonry Brick
35643     */
35644     clearAll : function()
35645     {
35646         this.bricks = [];
35647         //this.getChildContainer().dom.innerHTML = "";
35648         this.el.dom.innerHTML = '';
35649     },
35650     
35651     getSelected : function()
35652     {
35653         if (!this.selectedBrick) {
35654             return false;
35655         }
35656         
35657         return this.selectedBrick;
35658     }
35659 });
35660
35661 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35662     
35663     groups: {},
35664      /**
35665     * register a Masonry Layout
35666     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35667     */
35668     
35669     register : function(layout)
35670     {
35671         this.groups[layout.id] = layout;
35672     },
35673     /**
35674     * fetch a  Masonry Layout based on the masonry layout ID
35675     * @param {string} the masonry layout to add
35676     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35677     */
35678     
35679     get: function(layout_id) {
35680         if (typeof(this.groups[layout_id]) == 'undefined') {
35681             return false;
35682         }
35683         return this.groups[layout_id] ;
35684     }
35685     
35686     
35687     
35688 });
35689
35690  
35691
35692  /**
35693  *
35694  * This is based on 
35695  * http://masonry.desandro.com
35696  *
35697  * The idea is to render all the bricks based on vertical width...
35698  *
35699  * The original code extends 'outlayer' - we might need to use that....
35700  * 
35701  */
35702
35703
35704 /**
35705  * @class Roo.bootstrap.LayoutMasonryAuto
35706  * @extends Roo.bootstrap.Component
35707  * Bootstrap Layout Masonry class
35708  * 
35709  * @constructor
35710  * Create a new Element
35711  * @param {Object} config The config object
35712  */
35713
35714 Roo.bootstrap.LayoutMasonryAuto = function(config){
35715     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35716 };
35717
35718 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35719     
35720       /**
35721      * @cfg {Boolean} isFitWidth  - resize the width..
35722      */   
35723     isFitWidth : false,  // options..
35724     /**
35725      * @cfg {Boolean} isOriginLeft = left align?
35726      */   
35727     isOriginLeft : true,
35728     /**
35729      * @cfg {Boolean} isOriginTop = top align?
35730      */   
35731     isOriginTop : false,
35732     /**
35733      * @cfg {Boolean} isLayoutInstant = no animation?
35734      */   
35735     isLayoutInstant : false, // needed?
35736     /**
35737      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35738      */   
35739     isResizingContainer : true,
35740     /**
35741      * @cfg {Number} columnWidth  width of the columns 
35742      */   
35743     
35744     columnWidth : 0,
35745     
35746     /**
35747      * @cfg {Number} maxCols maximum number of columns
35748      */   
35749     
35750     maxCols: 0,
35751     /**
35752      * @cfg {Number} padHeight padding below box..
35753      */   
35754     
35755     padHeight : 10, 
35756     
35757     /**
35758      * @cfg {Boolean} isAutoInitial defalut true
35759      */   
35760     
35761     isAutoInitial : true, 
35762     
35763     // private?
35764     gutter : 0,
35765     
35766     containerWidth: 0,
35767     initialColumnWidth : 0,
35768     currentSize : null,
35769     
35770     colYs : null, // array.
35771     maxY : 0,
35772     padWidth: 10,
35773     
35774     
35775     tag: 'div',
35776     cls: '',
35777     bricks: null, //CompositeElement
35778     cols : 0, // array?
35779     // element : null, // wrapped now this.el
35780     _isLayoutInited : null, 
35781     
35782     
35783     getAutoCreate : function(){
35784         
35785         var cfg = {
35786             tag: this.tag,
35787             cls: 'blog-masonary-wrapper ' + this.cls,
35788             cn : {
35789                 cls : 'mas-boxes masonary'
35790             }
35791         };
35792         
35793         return cfg;
35794     },
35795     
35796     getChildContainer: function( )
35797     {
35798         if (this.boxesEl) {
35799             return this.boxesEl;
35800         }
35801         
35802         this.boxesEl = this.el.select('.mas-boxes').first();
35803         
35804         return this.boxesEl;
35805     },
35806     
35807     
35808     initEvents : function()
35809     {
35810         var _this = this;
35811         
35812         if(this.isAutoInitial){
35813             Roo.log('hook children rendered');
35814             this.on('childrenrendered', function() {
35815                 Roo.log('children rendered');
35816                 _this.initial();
35817             } ,this);
35818         }
35819         
35820     },
35821     
35822     initial : function()
35823     {
35824         this.reloadItems();
35825
35826         this.currentSize = this.el.getBox(true);
35827
35828         /// was window resize... - let's see if this works..
35829         Roo.EventManager.onWindowResize(this.resize, this); 
35830
35831         if(!this.isAutoInitial){
35832             this.layout();
35833             return;
35834         }
35835         
35836         this.layout.defer(500,this);
35837     },
35838     
35839     reloadItems: function()
35840     {
35841         this.bricks = this.el.select('.masonry-brick', true);
35842         
35843         this.bricks.each(function(b) {
35844             //Roo.log(b.getSize());
35845             if (!b.attr('originalwidth')) {
35846                 b.attr('originalwidth',  b.getSize().width);
35847             }
35848             
35849         });
35850         
35851         Roo.log(this.bricks.elements.length);
35852     },
35853     
35854     resize : function()
35855     {
35856         Roo.log('resize');
35857         var cs = this.el.getBox(true);
35858         
35859         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35860             Roo.log("no change in with or X");
35861             return;
35862         }
35863         this.currentSize = cs;
35864         this.layout();
35865     },
35866     
35867     layout : function()
35868     {
35869          Roo.log('layout');
35870         this._resetLayout();
35871         //this._manageStamps();
35872       
35873         // don't animate first layout
35874         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35875         this.layoutItems( isInstant );
35876       
35877         // flag for initalized
35878         this._isLayoutInited = true;
35879     },
35880     
35881     layoutItems : function( isInstant )
35882     {
35883         //var items = this._getItemsForLayout( this.items );
35884         // original code supports filtering layout items.. we just ignore it..
35885         
35886         this._layoutItems( this.bricks , isInstant );
35887       
35888         this._postLayout();
35889     },
35890     _layoutItems : function ( items , isInstant)
35891     {
35892        //this.fireEvent( 'layout', this, items );
35893     
35894
35895         if ( !items || !items.elements.length ) {
35896           // no items, emit event with empty array
35897             return;
35898         }
35899
35900         var queue = [];
35901         items.each(function(item) {
35902             Roo.log("layout item");
35903             Roo.log(item);
35904             // get x/y object from method
35905             var position = this._getItemLayoutPosition( item );
35906             // enqueue
35907             position.item = item;
35908             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35909             queue.push( position );
35910         }, this);
35911       
35912         this._processLayoutQueue( queue );
35913     },
35914     /** Sets position of item in DOM
35915     * @param {Element} item
35916     * @param {Number} x - horizontal position
35917     * @param {Number} y - vertical position
35918     * @param {Boolean} isInstant - disables transitions
35919     */
35920     _processLayoutQueue : function( queue )
35921     {
35922         for ( var i=0, len = queue.length; i < len; i++ ) {
35923             var obj = queue[i];
35924             obj.item.position('absolute');
35925             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35926         }
35927     },
35928       
35929     
35930     /**
35931     * Any logic you want to do after each layout,
35932     * i.e. size the container
35933     */
35934     _postLayout : function()
35935     {
35936         this.resizeContainer();
35937     },
35938     
35939     resizeContainer : function()
35940     {
35941         if ( !this.isResizingContainer ) {
35942             return;
35943         }
35944         var size = this._getContainerSize();
35945         if ( size ) {
35946             this.el.setSize(size.width,size.height);
35947             this.boxesEl.setSize(size.width,size.height);
35948         }
35949     },
35950     
35951     
35952     
35953     _resetLayout : function()
35954     {
35955         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35956         this.colWidth = this.el.getWidth();
35957         //this.gutter = this.el.getWidth(); 
35958         
35959         this.measureColumns();
35960
35961         // reset column Y
35962         var i = this.cols;
35963         this.colYs = [];
35964         while (i--) {
35965             this.colYs.push( 0 );
35966         }
35967     
35968         this.maxY = 0;
35969     },
35970
35971     measureColumns : function()
35972     {
35973         this.getContainerWidth();
35974       // if columnWidth is 0, default to outerWidth of first item
35975         if ( !this.columnWidth ) {
35976             var firstItem = this.bricks.first();
35977             Roo.log(firstItem);
35978             this.columnWidth  = this.containerWidth;
35979             if (firstItem && firstItem.attr('originalwidth') ) {
35980                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35981             }
35982             // columnWidth fall back to item of first element
35983             Roo.log("set column width?");
35984                         this.initialColumnWidth = this.columnWidth  ;
35985
35986             // if first elem has no width, default to size of container
35987             
35988         }
35989         
35990         
35991         if (this.initialColumnWidth) {
35992             this.columnWidth = this.initialColumnWidth;
35993         }
35994         
35995         
35996             
35997         // column width is fixed at the top - however if container width get's smaller we should
35998         // reduce it...
35999         
36000         // this bit calcs how man columns..
36001             
36002         var columnWidth = this.columnWidth += this.gutter;
36003       
36004         // calculate columns
36005         var containerWidth = this.containerWidth + this.gutter;
36006         
36007         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36008         // fix rounding errors, typically with gutters
36009         var excess = columnWidth - containerWidth % columnWidth;
36010         
36011         
36012         // if overshoot is less than a pixel, round up, otherwise floor it
36013         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36014         cols = Math[ mathMethod ]( cols );
36015         this.cols = Math.max( cols, 1 );
36016         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36017         
36018          // padding positioning..
36019         var totalColWidth = this.cols * this.columnWidth;
36020         var padavail = this.containerWidth - totalColWidth;
36021         // so for 2 columns - we need 3 'pads'
36022         
36023         var padNeeded = (1+this.cols) * this.padWidth;
36024         
36025         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36026         
36027         this.columnWidth += padExtra
36028         //this.padWidth = Math.floor(padavail /  ( this.cols));
36029         
36030         // adjust colum width so that padding is fixed??
36031         
36032         // we have 3 columns ... total = width * 3
36033         // we have X left over... that should be used by 
36034         
36035         //if (this.expandC) {
36036             
36037         //}
36038         
36039         
36040         
36041     },
36042     
36043     getContainerWidth : function()
36044     {
36045        /* // container is parent if fit width
36046         var container = this.isFitWidth ? this.element.parentNode : this.element;
36047         // check that this.size and size are there
36048         // IE8 triggers resize on body size change, so they might not be
36049         
36050         var size = getSize( container );  //FIXME
36051         this.containerWidth = size && size.innerWidth; //FIXME
36052         */
36053          
36054         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36055         
36056     },
36057     
36058     _getItemLayoutPosition : function( item )  // what is item?
36059     {
36060         // we resize the item to our columnWidth..
36061       
36062         item.setWidth(this.columnWidth);
36063         item.autoBoxAdjust  = false;
36064         
36065         var sz = item.getSize();
36066  
36067         // how many columns does this brick span
36068         var remainder = this.containerWidth % this.columnWidth;
36069         
36070         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36071         // round if off by 1 pixel, otherwise use ceil
36072         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36073         colSpan = Math.min( colSpan, this.cols );
36074         
36075         // normally this should be '1' as we dont' currently allow multi width columns..
36076         
36077         var colGroup = this._getColGroup( colSpan );
36078         // get the minimum Y value from the columns
36079         var minimumY = Math.min.apply( Math, colGroup );
36080         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36081         
36082         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36083          
36084         // position the brick
36085         var position = {
36086             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36087             y: this.currentSize.y + minimumY + this.padHeight
36088         };
36089         
36090         Roo.log(position);
36091         // apply setHeight to necessary columns
36092         var setHeight = minimumY + sz.height + this.padHeight;
36093         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36094         
36095         var setSpan = this.cols + 1 - colGroup.length;
36096         for ( var i = 0; i < setSpan; i++ ) {
36097           this.colYs[ shortColIndex + i ] = setHeight ;
36098         }
36099       
36100         return position;
36101     },
36102     
36103     /**
36104      * @param {Number} colSpan - number of columns the element spans
36105      * @returns {Array} colGroup
36106      */
36107     _getColGroup : function( colSpan )
36108     {
36109         if ( colSpan < 2 ) {
36110           // if brick spans only one column, use all the column Ys
36111           return this.colYs;
36112         }
36113       
36114         var colGroup = [];
36115         // how many different places could this brick fit horizontally
36116         var groupCount = this.cols + 1 - colSpan;
36117         // for each group potential horizontal position
36118         for ( var i = 0; i < groupCount; i++ ) {
36119           // make an array of colY values for that one group
36120           var groupColYs = this.colYs.slice( i, i + colSpan );
36121           // and get the max value of the array
36122           colGroup[i] = Math.max.apply( Math, groupColYs );
36123         }
36124         return colGroup;
36125     },
36126     /*
36127     _manageStamp : function( stamp )
36128     {
36129         var stampSize =  stamp.getSize();
36130         var offset = stamp.getBox();
36131         // get the columns that this stamp affects
36132         var firstX = this.isOriginLeft ? offset.x : offset.right;
36133         var lastX = firstX + stampSize.width;
36134         var firstCol = Math.floor( firstX / this.columnWidth );
36135         firstCol = Math.max( 0, firstCol );
36136         
36137         var lastCol = Math.floor( lastX / this.columnWidth );
36138         // lastCol should not go over if multiple of columnWidth #425
36139         lastCol -= lastX % this.columnWidth ? 0 : 1;
36140         lastCol = Math.min( this.cols - 1, lastCol );
36141         
36142         // set colYs to bottom of the stamp
36143         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36144             stampSize.height;
36145             
36146         for ( var i = firstCol; i <= lastCol; i++ ) {
36147           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36148         }
36149     },
36150     */
36151     
36152     _getContainerSize : function()
36153     {
36154         this.maxY = Math.max.apply( Math, this.colYs );
36155         var size = {
36156             height: this.maxY
36157         };
36158       
36159         if ( this.isFitWidth ) {
36160             size.width = this._getContainerFitWidth();
36161         }
36162       
36163         return size;
36164     },
36165     
36166     _getContainerFitWidth : function()
36167     {
36168         var unusedCols = 0;
36169         // count unused columns
36170         var i = this.cols;
36171         while ( --i ) {
36172           if ( this.colYs[i] !== 0 ) {
36173             break;
36174           }
36175           unusedCols++;
36176         }
36177         // fit container to columns that have been used
36178         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36179     },
36180     
36181     needsResizeLayout : function()
36182     {
36183         var previousWidth = this.containerWidth;
36184         this.getContainerWidth();
36185         return previousWidth !== this.containerWidth;
36186     }
36187  
36188 });
36189
36190  
36191
36192  /*
36193  * - LGPL
36194  *
36195  * element
36196  * 
36197  */
36198
36199 /**
36200  * @class Roo.bootstrap.MasonryBrick
36201  * @extends Roo.bootstrap.Component
36202  * Bootstrap MasonryBrick class
36203  * 
36204  * @constructor
36205  * Create a new MasonryBrick
36206  * @param {Object} config The config object
36207  */
36208
36209 Roo.bootstrap.MasonryBrick = function(config){
36210     
36211     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36212     
36213     Roo.bootstrap.MasonryBrick.register(this);
36214     
36215     this.addEvents({
36216         // raw events
36217         /**
36218          * @event click
36219          * When a MasonryBrick is clcik
36220          * @param {Roo.bootstrap.MasonryBrick} this
36221          * @param {Roo.EventObject} e
36222          */
36223         "click" : true
36224     });
36225 };
36226
36227 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36228     
36229     /**
36230      * @cfg {String} title
36231      */   
36232     title : '',
36233     /**
36234      * @cfg {String} html
36235      */   
36236     html : '',
36237     /**
36238      * @cfg {String} bgimage
36239      */   
36240     bgimage : '',
36241     /**
36242      * @cfg {String} videourl
36243      */   
36244     videourl : '',
36245     /**
36246      * @cfg {String} cls
36247      */   
36248     cls : '',
36249     /**
36250      * @cfg {String} href
36251      */   
36252     href : '',
36253     /**
36254      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36255      */   
36256     size : 'xs',
36257     
36258     /**
36259      * @cfg {String} placetitle (center|bottom)
36260      */   
36261     placetitle : '',
36262     
36263     /**
36264      * @cfg {Boolean} isFitContainer defalut true
36265      */   
36266     isFitContainer : true, 
36267     
36268     /**
36269      * @cfg {Boolean} preventDefault defalut false
36270      */   
36271     preventDefault : false, 
36272     
36273     /**
36274      * @cfg {Boolean} inverse defalut false
36275      */   
36276     maskInverse : false, 
36277     
36278     getAutoCreate : function()
36279     {
36280         if(!this.isFitContainer){
36281             return this.getSplitAutoCreate();
36282         }
36283         
36284         var cls = 'masonry-brick masonry-brick-full';
36285         
36286         if(this.href.length){
36287             cls += ' masonry-brick-link';
36288         }
36289         
36290         if(this.bgimage.length){
36291             cls += ' masonry-brick-image';
36292         }
36293         
36294         if(this.maskInverse){
36295             cls += ' mask-inverse';
36296         }
36297         
36298         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36299             cls += ' enable-mask';
36300         }
36301         
36302         if(this.size){
36303             cls += ' masonry-' + this.size + '-brick';
36304         }
36305         
36306         if(this.placetitle.length){
36307             
36308             switch (this.placetitle) {
36309                 case 'center' :
36310                     cls += ' masonry-center-title';
36311                     break;
36312                 case 'bottom' :
36313                     cls += ' masonry-bottom-title';
36314                     break;
36315                 default:
36316                     break;
36317             }
36318             
36319         } else {
36320             if(!this.html.length && !this.bgimage.length){
36321                 cls += ' masonry-center-title';
36322             }
36323
36324             if(!this.html.length && this.bgimage.length){
36325                 cls += ' masonry-bottom-title';
36326             }
36327         }
36328         
36329         if(this.cls){
36330             cls += ' ' + this.cls;
36331         }
36332         
36333         var cfg = {
36334             tag: (this.href.length) ? 'a' : 'div',
36335             cls: cls,
36336             cn: [
36337                 {
36338                     tag: 'div',
36339                     cls: 'masonry-brick-mask'
36340                 },
36341                 {
36342                     tag: 'div',
36343                     cls: 'masonry-brick-paragraph',
36344                     cn: []
36345                 }
36346             ]
36347         };
36348         
36349         if(this.href.length){
36350             cfg.href = this.href;
36351         }
36352         
36353         var cn = cfg.cn[1].cn;
36354         
36355         if(this.title.length){
36356             cn.push({
36357                 tag: 'h4',
36358                 cls: 'masonry-brick-title',
36359                 html: this.title
36360             });
36361         }
36362         
36363         if(this.html.length){
36364             cn.push({
36365                 tag: 'p',
36366                 cls: 'masonry-brick-text',
36367                 html: this.html
36368             });
36369         }
36370         
36371         if (!this.title.length && !this.html.length) {
36372             cfg.cn[1].cls += ' hide';
36373         }
36374         
36375         if(this.bgimage.length){
36376             cfg.cn.push({
36377                 tag: 'img',
36378                 cls: 'masonry-brick-image-view',
36379                 src: this.bgimage
36380             });
36381         }
36382         
36383         if(this.videourl.length){
36384             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36385             // youtube support only?
36386             cfg.cn.push({
36387                 tag: 'iframe',
36388                 cls: 'masonry-brick-image-view',
36389                 src: vurl,
36390                 frameborder : 0,
36391                 allowfullscreen : true
36392             });
36393         }
36394         
36395         return cfg;
36396         
36397     },
36398     
36399     getSplitAutoCreate : function()
36400     {
36401         var cls = 'masonry-brick masonry-brick-split';
36402         
36403         if(this.href.length){
36404             cls += ' masonry-brick-link';
36405         }
36406         
36407         if(this.bgimage.length){
36408             cls += ' masonry-brick-image';
36409         }
36410         
36411         if(this.size){
36412             cls += ' masonry-' + this.size + '-brick';
36413         }
36414         
36415         switch (this.placetitle) {
36416             case 'center' :
36417                 cls += ' masonry-center-title';
36418                 break;
36419             case 'bottom' :
36420                 cls += ' masonry-bottom-title';
36421                 break;
36422             default:
36423                 if(!this.bgimage.length){
36424                     cls += ' masonry-center-title';
36425                 }
36426
36427                 if(this.bgimage.length){
36428                     cls += ' masonry-bottom-title';
36429                 }
36430                 break;
36431         }
36432         
36433         if(this.cls){
36434             cls += ' ' + this.cls;
36435         }
36436         
36437         var cfg = {
36438             tag: (this.href.length) ? 'a' : 'div',
36439             cls: cls,
36440             cn: [
36441                 {
36442                     tag: 'div',
36443                     cls: 'masonry-brick-split-head',
36444                     cn: [
36445                         {
36446                             tag: 'div',
36447                             cls: 'masonry-brick-paragraph',
36448                             cn: []
36449                         }
36450                     ]
36451                 },
36452                 {
36453                     tag: 'div',
36454                     cls: 'masonry-brick-split-body',
36455                     cn: []
36456                 }
36457             ]
36458         };
36459         
36460         if(this.href.length){
36461             cfg.href = this.href;
36462         }
36463         
36464         if(this.title.length){
36465             cfg.cn[0].cn[0].cn.push({
36466                 tag: 'h4',
36467                 cls: 'masonry-brick-title',
36468                 html: this.title
36469             });
36470         }
36471         
36472         if(this.html.length){
36473             cfg.cn[1].cn.push({
36474                 tag: 'p',
36475                 cls: 'masonry-brick-text',
36476                 html: this.html
36477             });
36478         }
36479
36480         if(this.bgimage.length){
36481             cfg.cn[0].cn.push({
36482                 tag: 'img',
36483                 cls: 'masonry-brick-image-view',
36484                 src: this.bgimage
36485             });
36486         }
36487         
36488         if(this.videourl.length){
36489             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36490             // youtube support only?
36491             cfg.cn[0].cn.cn.push({
36492                 tag: 'iframe',
36493                 cls: 'masonry-brick-image-view',
36494                 src: vurl,
36495                 frameborder : 0,
36496                 allowfullscreen : true
36497             });
36498         }
36499         
36500         return cfg;
36501     },
36502     
36503     initEvents: function() 
36504     {
36505         switch (this.size) {
36506             case 'xs' :
36507                 this.x = 1;
36508                 this.y = 1;
36509                 break;
36510             case 'sm' :
36511                 this.x = 2;
36512                 this.y = 2;
36513                 break;
36514             case 'md' :
36515             case 'md-left' :
36516             case 'md-right' :
36517                 this.x = 3;
36518                 this.y = 3;
36519                 break;
36520             case 'tall' :
36521                 this.x = 2;
36522                 this.y = 3;
36523                 break;
36524             case 'wide' :
36525                 this.x = 3;
36526                 this.y = 2;
36527                 break;
36528             case 'wide-thin' :
36529                 this.x = 3;
36530                 this.y = 1;
36531                 break;
36532                         
36533             default :
36534                 break;
36535         }
36536         
36537         if(Roo.isTouch){
36538             this.el.on('touchstart', this.onTouchStart, this);
36539             this.el.on('touchmove', this.onTouchMove, this);
36540             this.el.on('touchend', this.onTouchEnd, this);
36541             this.el.on('contextmenu', this.onContextMenu, this);
36542         } else {
36543             this.el.on('mouseenter'  ,this.enter, this);
36544             this.el.on('mouseleave', this.leave, this);
36545             this.el.on('click', this.onClick, this);
36546         }
36547         
36548         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36549             this.parent().bricks.push(this);   
36550         }
36551         
36552     },
36553     
36554     onClick: function(e, el)
36555     {
36556         var time = this.endTimer - this.startTimer;
36557         // Roo.log(e.preventDefault());
36558         if(Roo.isTouch){
36559             if(time > 1000){
36560                 e.preventDefault();
36561                 return;
36562             }
36563         }
36564         
36565         if(!this.preventDefault){
36566             return;
36567         }
36568         
36569         e.preventDefault();
36570         
36571         if (this.activeClass != '') {
36572             this.selectBrick();
36573         }
36574         
36575         this.fireEvent('click', this, e);
36576     },
36577     
36578     enter: function(e, el)
36579     {
36580         e.preventDefault();
36581         
36582         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36583             return;
36584         }
36585         
36586         if(this.bgimage.length && this.html.length){
36587             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36588         }
36589     },
36590     
36591     leave: function(e, el)
36592     {
36593         e.preventDefault();
36594         
36595         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36596             return;
36597         }
36598         
36599         if(this.bgimage.length && this.html.length){
36600             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36601         }
36602     },
36603     
36604     onTouchStart: function(e, el)
36605     {
36606 //        e.preventDefault();
36607         
36608         this.touchmoved = false;
36609         
36610         if(!this.isFitContainer){
36611             return;
36612         }
36613         
36614         if(!this.bgimage.length || !this.html.length){
36615             return;
36616         }
36617         
36618         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36619         
36620         this.timer = new Date().getTime();
36621         
36622     },
36623     
36624     onTouchMove: function(e, el)
36625     {
36626         this.touchmoved = true;
36627     },
36628     
36629     onContextMenu : function(e,el)
36630     {
36631         e.preventDefault();
36632         e.stopPropagation();
36633         return false;
36634     },
36635     
36636     onTouchEnd: function(e, el)
36637     {
36638 //        e.preventDefault();
36639         
36640         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36641         
36642             this.leave(e,el);
36643             
36644             return;
36645         }
36646         
36647         if(!this.bgimage.length || !this.html.length){
36648             
36649             if(this.href.length){
36650                 window.location.href = this.href;
36651             }
36652             
36653             return;
36654         }
36655         
36656         if(!this.isFitContainer){
36657             return;
36658         }
36659         
36660         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36661         
36662         window.location.href = this.href;
36663     },
36664     
36665     //selection on single brick only
36666     selectBrick : function() {
36667         
36668         if (!this.parentId) {
36669             return;
36670         }
36671         
36672         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36673         var index = m.selectedBrick.indexOf(this.id);
36674         
36675         if ( index > -1) {
36676             m.selectedBrick.splice(index,1);
36677             this.el.removeClass(this.activeClass);
36678             return;
36679         }
36680         
36681         for(var i = 0; i < m.selectedBrick.length; i++) {
36682             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36683             b.el.removeClass(b.activeClass);
36684         }
36685         
36686         m.selectedBrick = [];
36687         
36688         m.selectedBrick.push(this.id);
36689         this.el.addClass(this.activeClass);
36690         return;
36691     },
36692     
36693     isSelected : function(){
36694         return this.el.hasClass(this.activeClass);
36695         
36696     }
36697 });
36698
36699 Roo.apply(Roo.bootstrap.MasonryBrick, {
36700     
36701     //groups: {},
36702     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36703      /**
36704     * register a Masonry Brick
36705     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36706     */
36707     
36708     register : function(brick)
36709     {
36710         //this.groups[brick.id] = brick;
36711         this.groups.add(brick.id, brick);
36712     },
36713     /**
36714     * fetch a  masonry brick based on the masonry brick ID
36715     * @param {string} the masonry brick to add
36716     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36717     */
36718     
36719     get: function(brick_id) 
36720     {
36721         // if (typeof(this.groups[brick_id]) == 'undefined') {
36722         //     return false;
36723         // }
36724         // return this.groups[brick_id] ;
36725         
36726         if(this.groups.key(brick_id)) {
36727             return this.groups.key(brick_id);
36728         }
36729         
36730         return false;
36731     }
36732     
36733     
36734     
36735 });
36736
36737  /*
36738  * - LGPL
36739  *
36740  * element
36741  * 
36742  */
36743
36744 /**
36745  * @class Roo.bootstrap.Brick
36746  * @extends Roo.bootstrap.Component
36747  * Bootstrap Brick class
36748  * 
36749  * @constructor
36750  * Create a new Brick
36751  * @param {Object} config The config object
36752  */
36753
36754 Roo.bootstrap.Brick = function(config){
36755     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36756     
36757     this.addEvents({
36758         // raw events
36759         /**
36760          * @event click
36761          * When a Brick is click
36762          * @param {Roo.bootstrap.Brick} this
36763          * @param {Roo.EventObject} e
36764          */
36765         "click" : true
36766     });
36767 };
36768
36769 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36770     
36771     /**
36772      * @cfg {String} title
36773      */   
36774     title : '',
36775     /**
36776      * @cfg {String} html
36777      */   
36778     html : '',
36779     /**
36780      * @cfg {String} bgimage
36781      */   
36782     bgimage : '',
36783     /**
36784      * @cfg {String} cls
36785      */   
36786     cls : '',
36787     /**
36788      * @cfg {String} href
36789      */   
36790     href : '',
36791     /**
36792      * @cfg {String} video
36793      */   
36794     video : '',
36795     /**
36796      * @cfg {Boolean} square
36797      */   
36798     square : true,
36799     
36800     getAutoCreate : function()
36801     {
36802         var cls = 'roo-brick';
36803         
36804         if(this.href.length){
36805             cls += ' roo-brick-link';
36806         }
36807         
36808         if(this.bgimage.length){
36809             cls += ' roo-brick-image';
36810         }
36811         
36812         if(!this.html.length && !this.bgimage.length){
36813             cls += ' roo-brick-center-title';
36814         }
36815         
36816         if(!this.html.length && this.bgimage.length){
36817             cls += ' roo-brick-bottom-title';
36818         }
36819         
36820         if(this.cls){
36821             cls += ' ' + this.cls;
36822         }
36823         
36824         var cfg = {
36825             tag: (this.href.length) ? 'a' : 'div',
36826             cls: cls,
36827             cn: [
36828                 {
36829                     tag: 'div',
36830                     cls: 'roo-brick-paragraph',
36831                     cn: []
36832                 }
36833             ]
36834         };
36835         
36836         if(this.href.length){
36837             cfg.href = this.href;
36838         }
36839         
36840         var cn = cfg.cn[0].cn;
36841         
36842         if(this.title.length){
36843             cn.push({
36844                 tag: 'h4',
36845                 cls: 'roo-brick-title',
36846                 html: this.title
36847             });
36848         }
36849         
36850         if(this.html.length){
36851             cn.push({
36852                 tag: 'p',
36853                 cls: 'roo-brick-text',
36854                 html: this.html
36855             });
36856         } else {
36857             cn.cls += ' hide';
36858         }
36859         
36860         if(this.bgimage.length){
36861             cfg.cn.push({
36862                 tag: 'img',
36863                 cls: 'roo-brick-image-view',
36864                 src: this.bgimage
36865             });
36866         }
36867         
36868         return cfg;
36869     },
36870     
36871     initEvents: function() 
36872     {
36873         if(this.title.length || this.html.length){
36874             this.el.on('mouseenter'  ,this.enter, this);
36875             this.el.on('mouseleave', this.leave, this);
36876         }
36877         
36878         Roo.EventManager.onWindowResize(this.resize, this); 
36879         
36880         if(this.bgimage.length){
36881             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36882             this.imageEl.on('load', this.onImageLoad, this);
36883             return;
36884         }
36885         
36886         this.resize();
36887     },
36888     
36889     onImageLoad : function()
36890     {
36891         this.resize();
36892     },
36893     
36894     resize : function()
36895     {
36896         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36897         
36898         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36899         
36900         if(this.bgimage.length){
36901             var image = this.el.select('.roo-brick-image-view', true).first();
36902             
36903             image.setWidth(paragraph.getWidth());
36904             
36905             if(this.square){
36906                 image.setHeight(paragraph.getWidth());
36907             }
36908             
36909             this.el.setHeight(image.getHeight());
36910             paragraph.setHeight(image.getHeight());
36911             
36912         }
36913         
36914     },
36915     
36916     enter: function(e, el)
36917     {
36918         e.preventDefault();
36919         
36920         if(this.bgimage.length){
36921             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36922             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36923         }
36924     },
36925     
36926     leave: function(e, el)
36927     {
36928         e.preventDefault();
36929         
36930         if(this.bgimage.length){
36931             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36932             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36933         }
36934     }
36935     
36936 });
36937
36938  
36939
36940  /*
36941  * - LGPL
36942  *
36943  * Number field 
36944  */
36945
36946 /**
36947  * @class Roo.bootstrap.NumberField
36948  * @extends Roo.bootstrap.Input
36949  * Bootstrap NumberField class
36950  * 
36951  * 
36952  * 
36953  * 
36954  * @constructor
36955  * Create a new NumberField
36956  * @param {Object} config The config object
36957  */
36958
36959 Roo.bootstrap.NumberField = function(config){
36960     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36961 };
36962
36963 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36964     
36965     /**
36966      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36967      */
36968     allowDecimals : true,
36969     /**
36970      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36971      */
36972     decimalSeparator : ".",
36973     /**
36974      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36975      */
36976     decimalPrecision : 2,
36977     /**
36978      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36979      */
36980     allowNegative : true,
36981     
36982     /**
36983      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36984      */
36985     allowZero: true,
36986     /**
36987      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36988      */
36989     minValue : Number.NEGATIVE_INFINITY,
36990     /**
36991      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36992      */
36993     maxValue : Number.MAX_VALUE,
36994     /**
36995      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36996      */
36997     minText : "The minimum value for this field is {0}",
36998     /**
36999      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37000      */
37001     maxText : "The maximum value for this field is {0}",
37002     /**
37003      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37004      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37005      */
37006     nanText : "{0} is not a valid number",
37007     /**
37008      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37009      */
37010     thousandsDelimiter : false,
37011     /**
37012      * @cfg {String} valueAlign alignment of value
37013      */
37014     valueAlign : "left",
37015
37016     getAutoCreate : function()
37017     {
37018         var hiddenInput = {
37019             tag: 'input',
37020             type: 'hidden',
37021             id: Roo.id(),
37022             cls: 'hidden-number-input'
37023         };
37024         
37025         if (this.name) {
37026             hiddenInput.name = this.name;
37027         }
37028         
37029         this.name = '';
37030         
37031         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37032         
37033         this.name = hiddenInput.name;
37034         
37035         if(cfg.cn.length > 0) {
37036             cfg.cn.push(hiddenInput);
37037         }
37038         
37039         return cfg;
37040     },
37041
37042     // private
37043     initEvents : function()
37044     {   
37045         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37046         
37047         var allowed = "0123456789";
37048         
37049         if(this.allowDecimals){
37050             allowed += this.decimalSeparator;
37051         }
37052         
37053         if(this.allowNegative){
37054             allowed += "-";
37055         }
37056         
37057         if(this.thousandsDelimiter) {
37058             allowed += ",";
37059         }
37060         
37061         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37062         
37063         var keyPress = function(e){
37064             
37065             var k = e.getKey();
37066             
37067             var c = e.getCharCode();
37068             
37069             if(
37070                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37071                     allowed.indexOf(String.fromCharCode(c)) === -1
37072             ){
37073                 e.stopEvent();
37074                 return;
37075             }
37076             
37077             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37078                 return;
37079             }
37080             
37081             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37082                 e.stopEvent();
37083             }
37084         };
37085         
37086         this.el.on("keypress", keyPress, this);
37087     },
37088     
37089     validateValue : function(value)
37090     {
37091         
37092         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37093             return false;
37094         }
37095         
37096         var num = this.parseValue(value);
37097         
37098         if(isNaN(num)){
37099             this.markInvalid(String.format(this.nanText, value));
37100             return false;
37101         }
37102         
37103         if(num < this.minValue){
37104             this.markInvalid(String.format(this.minText, this.minValue));
37105             return false;
37106         }
37107         
37108         if(num > this.maxValue){
37109             this.markInvalid(String.format(this.maxText, this.maxValue));
37110             return false;
37111         }
37112         
37113         return true;
37114     },
37115
37116     getValue : function()
37117     {
37118         var v = this.hiddenEl().getValue();
37119         
37120         return this.fixPrecision(this.parseValue(v));
37121     },
37122
37123     parseValue : function(value)
37124     {
37125         if(this.thousandsDelimiter) {
37126             value += "";
37127             r = new RegExp(",", "g");
37128             value = value.replace(r, "");
37129         }
37130         
37131         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37132         return isNaN(value) ? '' : value;
37133     },
37134
37135     fixPrecision : function(value)
37136     {
37137         if(this.thousandsDelimiter) {
37138             value += "";
37139             r = new RegExp(",", "g");
37140             value = value.replace(r, "");
37141         }
37142         
37143         var nan = isNaN(value);
37144         
37145         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37146             return nan ? '' : value;
37147         }
37148         return parseFloat(value).toFixed(this.decimalPrecision);
37149     },
37150
37151     setValue : function(v)
37152     {
37153         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37154         
37155         this.value = v;
37156         
37157         if(this.rendered){
37158             
37159             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37160             
37161             this.inputEl().dom.value = (v == '') ? '' :
37162                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37163             
37164             if(!this.allowZero && v === '0') {
37165                 this.hiddenEl().dom.value = '';
37166                 this.inputEl().dom.value = '';
37167             }
37168             
37169             this.validate();
37170         }
37171     },
37172
37173     decimalPrecisionFcn : function(v)
37174     {
37175         return Math.floor(v);
37176     },
37177
37178     beforeBlur : function()
37179     {
37180         var v = this.parseValue(this.getRawValue());
37181         
37182         if(v || v === 0 || v === ''){
37183             this.setValue(v);
37184         }
37185     },
37186     
37187     hiddenEl : function()
37188     {
37189         return this.el.select('input.hidden-number-input',true).first();
37190     }
37191     
37192 });
37193
37194  
37195
37196 /*
37197 * Licence: LGPL
37198 */
37199
37200 /**
37201  * @class Roo.bootstrap.DocumentSlider
37202  * @extends Roo.bootstrap.Component
37203  * Bootstrap DocumentSlider class
37204  * 
37205  * @constructor
37206  * Create a new DocumentViewer
37207  * @param {Object} config The config object
37208  */
37209
37210 Roo.bootstrap.DocumentSlider = function(config){
37211     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37212     
37213     this.files = [];
37214     
37215     this.addEvents({
37216         /**
37217          * @event initial
37218          * Fire after initEvent
37219          * @param {Roo.bootstrap.DocumentSlider} this
37220          */
37221         "initial" : true,
37222         /**
37223          * @event update
37224          * Fire after update
37225          * @param {Roo.bootstrap.DocumentSlider} this
37226          */
37227         "update" : true,
37228         /**
37229          * @event click
37230          * Fire after click
37231          * @param {Roo.bootstrap.DocumentSlider} this
37232          */
37233         "click" : true
37234     });
37235 };
37236
37237 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37238     
37239     files : false,
37240     
37241     indicator : 0,
37242     
37243     getAutoCreate : function()
37244     {
37245         var cfg = {
37246             tag : 'div',
37247             cls : 'roo-document-slider',
37248             cn : [
37249                 {
37250                     tag : 'div',
37251                     cls : 'roo-document-slider-header',
37252                     cn : [
37253                         {
37254                             tag : 'div',
37255                             cls : 'roo-document-slider-header-title'
37256                         }
37257                     ]
37258                 },
37259                 {
37260                     tag : 'div',
37261                     cls : 'roo-document-slider-body',
37262                     cn : [
37263                         {
37264                             tag : 'div',
37265                             cls : 'roo-document-slider-prev',
37266                             cn : [
37267                                 {
37268                                     tag : 'i',
37269                                     cls : 'fa fa-chevron-left'
37270                                 }
37271                             ]
37272                         },
37273                         {
37274                             tag : 'div',
37275                             cls : 'roo-document-slider-thumb',
37276                             cn : [
37277                                 {
37278                                     tag : 'img',
37279                                     cls : 'roo-document-slider-image'
37280                                 }
37281                             ]
37282                         },
37283                         {
37284                             tag : 'div',
37285                             cls : 'roo-document-slider-next',
37286                             cn : [
37287                                 {
37288                                     tag : 'i',
37289                                     cls : 'fa fa-chevron-right'
37290                                 }
37291                             ]
37292                         }
37293                     ]
37294                 }
37295             ]
37296         };
37297         
37298         return cfg;
37299     },
37300     
37301     initEvents : function()
37302     {
37303         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37304         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37305         
37306         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37307         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37308         
37309         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37310         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37311         
37312         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37313         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37314         
37315         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37316         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37317         
37318         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37319         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37320         
37321         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37322         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37323         
37324         this.thumbEl.on('click', this.onClick, this);
37325         
37326         this.prevIndicator.on('click', this.prev, this);
37327         
37328         this.nextIndicator.on('click', this.next, this);
37329         
37330     },
37331     
37332     initial : function()
37333     {
37334         if(this.files.length){
37335             this.indicator = 1;
37336             this.update()
37337         }
37338         
37339         this.fireEvent('initial', this);
37340     },
37341     
37342     update : function()
37343     {
37344         this.imageEl.attr('src', this.files[this.indicator - 1]);
37345         
37346         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37347         
37348         this.prevIndicator.show();
37349         
37350         if(this.indicator == 1){
37351             this.prevIndicator.hide();
37352         }
37353         
37354         this.nextIndicator.show();
37355         
37356         if(this.indicator == this.files.length){
37357             this.nextIndicator.hide();
37358         }
37359         
37360         this.thumbEl.scrollTo('top');
37361         
37362         this.fireEvent('update', this);
37363     },
37364     
37365     onClick : function(e)
37366     {
37367         e.preventDefault();
37368         
37369         this.fireEvent('click', this);
37370     },
37371     
37372     prev : function(e)
37373     {
37374         e.preventDefault();
37375         
37376         this.indicator = Math.max(1, this.indicator - 1);
37377         
37378         this.update();
37379     },
37380     
37381     next : function(e)
37382     {
37383         e.preventDefault();
37384         
37385         this.indicator = Math.min(this.files.length, this.indicator + 1);
37386         
37387         this.update();
37388     }
37389 });
37390 /*
37391  * - LGPL
37392  *
37393  * RadioSet
37394  *
37395  *
37396  */
37397
37398 /**
37399  * @class Roo.bootstrap.RadioSet
37400  * @extends Roo.bootstrap.Input
37401  * Bootstrap RadioSet class
37402  * @cfg {String} indicatorpos (left|right) default left
37403  * @cfg {Boolean} inline (true|false) inline the element (default true)
37404  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37405  * @constructor
37406  * Create a new RadioSet
37407  * @param {Object} config The config object
37408  */
37409
37410 Roo.bootstrap.RadioSet = function(config){
37411     
37412     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37413     
37414     this.radioes = [];
37415     
37416     Roo.bootstrap.RadioSet.register(this);
37417     
37418     this.addEvents({
37419         /**
37420         * @event check
37421         * Fires when the element is checked or unchecked.
37422         * @param {Roo.bootstrap.RadioSet} this This radio
37423         * @param {Roo.bootstrap.Radio} item The checked item
37424         */
37425        check : true,
37426        /**
37427         * @event click
37428         * Fires when the element is click.
37429         * @param {Roo.bootstrap.RadioSet} this This radio set
37430         * @param {Roo.bootstrap.Radio} item The checked item
37431         * @param {Roo.EventObject} e The event object
37432         */
37433        click : true
37434     });
37435     
37436 };
37437
37438 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37439
37440     radioes : false,
37441     
37442     inline : true,
37443     
37444     weight : '',
37445     
37446     indicatorpos : 'left',
37447     
37448     getAutoCreate : function()
37449     {
37450         var label = {
37451             tag : 'label',
37452             cls : 'roo-radio-set-label',
37453             cn : [
37454                 {
37455                     tag : 'span',
37456                     html : this.fieldLabel
37457                 }
37458             ]
37459         };
37460         if (Roo.bootstrap.version == 3) {
37461             
37462             
37463             if(this.indicatorpos == 'left'){
37464                 label.cn.unshift({
37465                     tag : 'i',
37466                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37467                     tooltip : 'This field is required'
37468                 });
37469             } else {
37470                 label.cn.push({
37471                     tag : 'i',
37472                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37473                     tooltip : 'This field is required'
37474                 });
37475             }
37476         }
37477         var items = {
37478             tag : 'div',
37479             cls : 'roo-radio-set-items'
37480         };
37481         
37482         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37483         
37484         if (align === 'left' && this.fieldLabel.length) {
37485             
37486             items = {
37487                 cls : "roo-radio-set-right", 
37488                 cn: [
37489                     items
37490                 ]
37491             };
37492             
37493             if(this.labelWidth > 12){
37494                 label.style = "width: " + this.labelWidth + 'px';
37495             }
37496             
37497             if(this.labelWidth < 13 && this.labelmd == 0){
37498                 this.labelmd = this.labelWidth;
37499             }
37500             
37501             if(this.labellg > 0){
37502                 label.cls += ' col-lg-' + this.labellg;
37503                 items.cls += ' col-lg-' + (12 - this.labellg);
37504             }
37505             
37506             if(this.labelmd > 0){
37507                 label.cls += ' col-md-' + this.labelmd;
37508                 items.cls += ' col-md-' + (12 - this.labelmd);
37509             }
37510             
37511             if(this.labelsm > 0){
37512                 label.cls += ' col-sm-' + this.labelsm;
37513                 items.cls += ' col-sm-' + (12 - this.labelsm);
37514             }
37515             
37516             if(this.labelxs > 0){
37517                 label.cls += ' col-xs-' + this.labelxs;
37518                 items.cls += ' col-xs-' + (12 - this.labelxs);
37519             }
37520         }
37521         
37522         var cfg = {
37523             tag : 'div',
37524             cls : 'roo-radio-set',
37525             cn : [
37526                 {
37527                     tag : 'input',
37528                     cls : 'roo-radio-set-input',
37529                     type : 'hidden',
37530                     name : this.name,
37531                     value : this.value ? this.value :  ''
37532                 },
37533                 label,
37534                 items
37535             ]
37536         };
37537         
37538         if(this.weight.length){
37539             cfg.cls += ' roo-radio-' + this.weight;
37540         }
37541         
37542         if(this.inline) {
37543             cfg.cls += ' roo-radio-set-inline';
37544         }
37545         
37546         var settings=this;
37547         ['xs','sm','md','lg'].map(function(size){
37548             if (settings[size]) {
37549                 cfg.cls += ' col-' + size + '-' + settings[size];
37550             }
37551         });
37552         
37553         return cfg;
37554         
37555     },
37556
37557     initEvents : function()
37558     {
37559         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37560         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37561         
37562         if(!this.fieldLabel.length){
37563             this.labelEl.hide();
37564         }
37565         
37566         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37567         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37568         
37569         this.indicator = this.indicatorEl();
37570         
37571         if(this.indicator){
37572             this.indicator.addClass('invisible');
37573         }
37574         
37575         this.originalValue = this.getValue();
37576         
37577     },
37578     
37579     inputEl: function ()
37580     {
37581         return this.el.select('.roo-radio-set-input', true).first();
37582     },
37583     
37584     getChildContainer : function()
37585     {
37586         return this.itemsEl;
37587     },
37588     
37589     register : function(item)
37590     {
37591         this.radioes.push(item);
37592         
37593     },
37594     
37595     validate : function()
37596     {   
37597         if(this.getVisibilityEl().hasClass('hidden')){
37598             return true;
37599         }
37600         
37601         var valid = false;
37602         
37603         Roo.each(this.radioes, function(i){
37604             if(!i.checked){
37605                 return;
37606             }
37607             
37608             valid = true;
37609             return false;
37610         });
37611         
37612         if(this.allowBlank) {
37613             return true;
37614         }
37615         
37616         if(this.disabled || valid){
37617             this.markValid();
37618             return true;
37619         }
37620         
37621         this.markInvalid();
37622         return false;
37623         
37624     },
37625     
37626     markValid : function()
37627     {
37628         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37629             this.indicatorEl().removeClass('visible');
37630             this.indicatorEl().addClass('invisible');
37631         }
37632         
37633         
37634         if (Roo.bootstrap.version == 3) {
37635             this.el.removeClass([this.invalidClass, this.validClass]);
37636             this.el.addClass(this.validClass);
37637         } else {
37638             this.el.removeClass(['is-invalid','is-valid']);
37639             this.el.addClass(['is-valid']);
37640         }
37641         this.fireEvent('valid', this);
37642     },
37643     
37644     markInvalid : function(msg)
37645     {
37646         if(this.allowBlank || this.disabled){
37647             return;
37648         }
37649         
37650         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37651             this.indicatorEl().removeClass('invisible');
37652             this.indicatorEl().addClass('visible');
37653         }
37654         if (Roo.bootstrap.version == 3) {
37655             this.el.removeClass([this.invalidClass, this.validClass]);
37656             this.el.addClass(this.invalidClass);
37657         } else {
37658             this.el.removeClass(['is-invalid','is-valid']);
37659             this.el.addClass(['is-invalid']);
37660         }
37661         
37662         this.fireEvent('invalid', this, msg);
37663         
37664     },
37665     
37666     setValue : function(v, suppressEvent)
37667     {   
37668         if(this.value === v){
37669             return;
37670         }
37671         
37672         this.value = v;
37673         
37674         if(this.rendered){
37675             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37676         }
37677         
37678         Roo.each(this.radioes, function(i){
37679             i.checked = false;
37680             i.el.removeClass('checked');
37681         });
37682         
37683         Roo.each(this.radioes, function(i){
37684             
37685             if(i.value === v || i.value.toString() === v.toString()){
37686                 i.checked = true;
37687                 i.el.addClass('checked');
37688                 
37689                 if(suppressEvent !== true){
37690                     this.fireEvent('check', this, i);
37691                 }
37692                 
37693                 return false;
37694             }
37695             
37696         }, this);
37697         
37698         this.validate();
37699     },
37700     
37701     clearInvalid : function(){
37702         
37703         if(!this.el || this.preventMark){
37704             return;
37705         }
37706         
37707         this.el.removeClass([this.invalidClass]);
37708         
37709         this.fireEvent('valid', this);
37710     }
37711     
37712 });
37713
37714 Roo.apply(Roo.bootstrap.RadioSet, {
37715     
37716     groups: {},
37717     
37718     register : function(set)
37719     {
37720         this.groups[set.name] = set;
37721     },
37722     
37723     get: function(name) 
37724     {
37725         if (typeof(this.groups[name]) == 'undefined') {
37726             return false;
37727         }
37728         
37729         return this.groups[name] ;
37730     }
37731     
37732 });
37733 /*
37734  * Based on:
37735  * Ext JS Library 1.1.1
37736  * Copyright(c) 2006-2007, Ext JS, LLC.
37737  *
37738  * Originally Released Under LGPL - original licence link has changed is not relivant.
37739  *
37740  * Fork - LGPL
37741  * <script type="text/javascript">
37742  */
37743
37744
37745 /**
37746  * @class Roo.bootstrap.SplitBar
37747  * @extends Roo.util.Observable
37748  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37749  * <br><br>
37750  * Usage:
37751  * <pre><code>
37752 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37753                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37754 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37755 split.minSize = 100;
37756 split.maxSize = 600;
37757 split.animate = true;
37758 split.on('moved', splitterMoved);
37759 </code></pre>
37760  * @constructor
37761  * Create a new SplitBar
37762  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37763  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37764  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37765  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37766                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37767                         position of the SplitBar).
37768  */
37769 Roo.bootstrap.SplitBar = function(cfg){
37770     
37771     /** @private */
37772     
37773     //{
37774     //  dragElement : elm
37775     //  resizingElement: el,
37776         // optional..
37777     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37778     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37779         // existingProxy ???
37780     //}
37781     
37782     this.el = Roo.get(cfg.dragElement, true);
37783     this.el.dom.unselectable = "on";
37784     /** @private */
37785     this.resizingEl = Roo.get(cfg.resizingElement, true);
37786
37787     /**
37788      * @private
37789      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37790      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37791      * @type Number
37792      */
37793     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37794     
37795     /**
37796      * The minimum size of the resizing element. (Defaults to 0)
37797      * @type Number
37798      */
37799     this.minSize = 0;
37800     
37801     /**
37802      * The maximum size of the resizing element. (Defaults to 2000)
37803      * @type Number
37804      */
37805     this.maxSize = 2000;
37806     
37807     /**
37808      * Whether to animate the transition to the new size
37809      * @type Boolean
37810      */
37811     this.animate = false;
37812     
37813     /**
37814      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37815      * @type Boolean
37816      */
37817     this.useShim = false;
37818     
37819     /** @private */
37820     this.shim = null;
37821     
37822     if(!cfg.existingProxy){
37823         /** @private */
37824         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37825     }else{
37826         this.proxy = Roo.get(cfg.existingProxy).dom;
37827     }
37828     /** @private */
37829     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37830     
37831     /** @private */
37832     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37833     
37834     /** @private */
37835     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37836     
37837     /** @private */
37838     this.dragSpecs = {};
37839     
37840     /**
37841      * @private The adapter to use to positon and resize elements
37842      */
37843     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37844     this.adapter.init(this);
37845     
37846     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37847         /** @private */
37848         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37849         this.el.addClass("roo-splitbar-h");
37850     }else{
37851         /** @private */
37852         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37853         this.el.addClass("roo-splitbar-v");
37854     }
37855     
37856     this.addEvents({
37857         /**
37858          * @event resize
37859          * Fires when the splitter is moved (alias for {@link #event-moved})
37860          * @param {Roo.bootstrap.SplitBar} this
37861          * @param {Number} newSize the new width or height
37862          */
37863         "resize" : true,
37864         /**
37865          * @event moved
37866          * Fires when the splitter is moved
37867          * @param {Roo.bootstrap.SplitBar} this
37868          * @param {Number} newSize the new width or height
37869          */
37870         "moved" : true,
37871         /**
37872          * @event beforeresize
37873          * Fires before the splitter is dragged
37874          * @param {Roo.bootstrap.SplitBar} this
37875          */
37876         "beforeresize" : true,
37877
37878         "beforeapply" : true
37879     });
37880
37881     Roo.util.Observable.call(this);
37882 };
37883
37884 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37885     onStartProxyDrag : function(x, y){
37886         this.fireEvent("beforeresize", this);
37887         if(!this.overlay){
37888             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37889             o.unselectable();
37890             o.enableDisplayMode("block");
37891             // all splitbars share the same overlay
37892             Roo.bootstrap.SplitBar.prototype.overlay = o;
37893         }
37894         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37895         this.overlay.show();
37896         Roo.get(this.proxy).setDisplayed("block");
37897         var size = this.adapter.getElementSize(this);
37898         this.activeMinSize = this.getMinimumSize();;
37899         this.activeMaxSize = this.getMaximumSize();;
37900         var c1 = size - this.activeMinSize;
37901         var c2 = Math.max(this.activeMaxSize - size, 0);
37902         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37903             this.dd.resetConstraints();
37904             this.dd.setXConstraint(
37905                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37906                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37907             );
37908             this.dd.setYConstraint(0, 0);
37909         }else{
37910             this.dd.resetConstraints();
37911             this.dd.setXConstraint(0, 0);
37912             this.dd.setYConstraint(
37913                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37914                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37915             );
37916          }
37917         this.dragSpecs.startSize = size;
37918         this.dragSpecs.startPoint = [x, y];
37919         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37920     },
37921     
37922     /** 
37923      * @private Called after the drag operation by the DDProxy
37924      */
37925     onEndProxyDrag : function(e){
37926         Roo.get(this.proxy).setDisplayed(false);
37927         var endPoint = Roo.lib.Event.getXY(e);
37928         if(this.overlay){
37929             this.overlay.hide();
37930         }
37931         var newSize;
37932         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37933             newSize = this.dragSpecs.startSize + 
37934                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37935                     endPoint[0] - this.dragSpecs.startPoint[0] :
37936                     this.dragSpecs.startPoint[0] - endPoint[0]
37937                 );
37938         }else{
37939             newSize = this.dragSpecs.startSize + 
37940                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37941                     endPoint[1] - this.dragSpecs.startPoint[1] :
37942                     this.dragSpecs.startPoint[1] - endPoint[1]
37943                 );
37944         }
37945         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37946         if(newSize != this.dragSpecs.startSize){
37947             if(this.fireEvent('beforeapply', this, newSize) !== false){
37948                 this.adapter.setElementSize(this, newSize);
37949                 this.fireEvent("moved", this, newSize);
37950                 this.fireEvent("resize", this, newSize);
37951             }
37952         }
37953     },
37954     
37955     /**
37956      * Get the adapter this SplitBar uses
37957      * @return The adapter object
37958      */
37959     getAdapter : function(){
37960         return this.adapter;
37961     },
37962     
37963     /**
37964      * Set the adapter this SplitBar uses
37965      * @param {Object} adapter A SplitBar adapter object
37966      */
37967     setAdapter : function(adapter){
37968         this.adapter = adapter;
37969         this.adapter.init(this);
37970     },
37971     
37972     /**
37973      * Gets the minimum size for the resizing element
37974      * @return {Number} The minimum size
37975      */
37976     getMinimumSize : function(){
37977         return this.minSize;
37978     },
37979     
37980     /**
37981      * Sets the minimum size for the resizing element
37982      * @param {Number} minSize The minimum size
37983      */
37984     setMinimumSize : function(minSize){
37985         this.minSize = minSize;
37986     },
37987     
37988     /**
37989      * Gets the maximum size for the resizing element
37990      * @return {Number} The maximum size
37991      */
37992     getMaximumSize : function(){
37993         return this.maxSize;
37994     },
37995     
37996     /**
37997      * Sets the maximum size for the resizing element
37998      * @param {Number} maxSize The maximum size
37999      */
38000     setMaximumSize : function(maxSize){
38001         this.maxSize = maxSize;
38002     },
38003     
38004     /**
38005      * Sets the initialize size for the resizing element
38006      * @param {Number} size The initial size
38007      */
38008     setCurrentSize : function(size){
38009         var oldAnimate = this.animate;
38010         this.animate = false;
38011         this.adapter.setElementSize(this, size);
38012         this.animate = oldAnimate;
38013     },
38014     
38015     /**
38016      * Destroy this splitbar. 
38017      * @param {Boolean} removeEl True to remove the element
38018      */
38019     destroy : function(removeEl){
38020         if(this.shim){
38021             this.shim.remove();
38022         }
38023         this.dd.unreg();
38024         this.proxy.parentNode.removeChild(this.proxy);
38025         if(removeEl){
38026             this.el.remove();
38027         }
38028     }
38029 });
38030
38031 /**
38032  * @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.
38033  */
38034 Roo.bootstrap.SplitBar.createProxy = function(dir){
38035     var proxy = new Roo.Element(document.createElement("div"));
38036     proxy.unselectable();
38037     var cls = 'roo-splitbar-proxy';
38038     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38039     document.body.appendChild(proxy.dom);
38040     return proxy.dom;
38041 };
38042
38043 /** 
38044  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38045  * Default Adapter. It assumes the splitter and resizing element are not positioned
38046  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38047  */
38048 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38049 };
38050
38051 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38052     // do nothing for now
38053     init : function(s){
38054     
38055     },
38056     /**
38057      * Called before drag operations to get the current size of the resizing element. 
38058      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38059      */
38060      getElementSize : function(s){
38061         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38062             return s.resizingEl.getWidth();
38063         }else{
38064             return s.resizingEl.getHeight();
38065         }
38066     },
38067     
38068     /**
38069      * Called after drag operations to set the size of the resizing element.
38070      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38071      * @param {Number} newSize The new size to set
38072      * @param {Function} onComplete A function to be invoked when resizing is complete
38073      */
38074     setElementSize : function(s, newSize, onComplete){
38075         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38076             if(!s.animate){
38077                 s.resizingEl.setWidth(newSize);
38078                 if(onComplete){
38079                     onComplete(s, newSize);
38080                 }
38081             }else{
38082                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38083             }
38084         }else{
38085             
38086             if(!s.animate){
38087                 s.resizingEl.setHeight(newSize);
38088                 if(onComplete){
38089                     onComplete(s, newSize);
38090                 }
38091             }else{
38092                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38093             }
38094         }
38095     }
38096 };
38097
38098 /** 
38099  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38100  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38101  * Adapter that  moves the splitter element to align with the resized sizing element. 
38102  * Used with an absolute positioned SplitBar.
38103  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38104  * document.body, make sure you assign an id to the body element.
38105  */
38106 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38107     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38108     this.container = Roo.get(container);
38109 };
38110
38111 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38112     init : function(s){
38113         this.basic.init(s);
38114     },
38115     
38116     getElementSize : function(s){
38117         return this.basic.getElementSize(s);
38118     },
38119     
38120     setElementSize : function(s, newSize, onComplete){
38121         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38122     },
38123     
38124     moveSplitter : function(s){
38125         var yes = Roo.bootstrap.SplitBar;
38126         switch(s.placement){
38127             case yes.LEFT:
38128                 s.el.setX(s.resizingEl.getRight());
38129                 break;
38130             case yes.RIGHT:
38131                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38132                 break;
38133             case yes.TOP:
38134                 s.el.setY(s.resizingEl.getBottom());
38135                 break;
38136             case yes.BOTTOM:
38137                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38138                 break;
38139         }
38140     }
38141 };
38142
38143 /**
38144  * Orientation constant - Create a vertical SplitBar
38145  * @static
38146  * @type Number
38147  */
38148 Roo.bootstrap.SplitBar.VERTICAL = 1;
38149
38150 /**
38151  * Orientation constant - Create a horizontal SplitBar
38152  * @static
38153  * @type Number
38154  */
38155 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38156
38157 /**
38158  * Placement constant - The resizing element is to the left of the splitter element
38159  * @static
38160  * @type Number
38161  */
38162 Roo.bootstrap.SplitBar.LEFT = 1;
38163
38164 /**
38165  * Placement constant - The resizing element is to the right of the splitter element
38166  * @static
38167  * @type Number
38168  */
38169 Roo.bootstrap.SplitBar.RIGHT = 2;
38170
38171 /**
38172  * Placement constant - The resizing element is positioned above the splitter element
38173  * @static
38174  * @type Number
38175  */
38176 Roo.bootstrap.SplitBar.TOP = 3;
38177
38178 /**
38179  * Placement constant - The resizing element is positioned under splitter element
38180  * @static
38181  * @type Number
38182  */
38183 Roo.bootstrap.SplitBar.BOTTOM = 4;
38184 Roo.namespace("Roo.bootstrap.layout");/*
38185  * Based on:
38186  * Ext JS Library 1.1.1
38187  * Copyright(c) 2006-2007, Ext JS, LLC.
38188  *
38189  * Originally Released Under LGPL - original licence link has changed is not relivant.
38190  *
38191  * Fork - LGPL
38192  * <script type="text/javascript">
38193  */
38194
38195 /**
38196  * @class Roo.bootstrap.layout.Manager
38197  * @extends Roo.bootstrap.Component
38198  * Base class for layout managers.
38199  */
38200 Roo.bootstrap.layout.Manager = function(config)
38201 {
38202     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38203
38204
38205
38206
38207
38208     /** false to disable window resize monitoring @type Boolean */
38209     this.monitorWindowResize = true;
38210     this.regions = {};
38211     this.addEvents({
38212         /**
38213          * @event layout
38214          * Fires when a layout is performed.
38215          * @param {Roo.LayoutManager} this
38216          */
38217         "layout" : true,
38218         /**
38219          * @event regionresized
38220          * Fires when the user resizes a region.
38221          * @param {Roo.LayoutRegion} region The resized region
38222          * @param {Number} newSize The new size (width for east/west, height for north/south)
38223          */
38224         "regionresized" : true,
38225         /**
38226          * @event regioncollapsed
38227          * Fires when a region is collapsed.
38228          * @param {Roo.LayoutRegion} region The collapsed region
38229          */
38230         "regioncollapsed" : true,
38231         /**
38232          * @event regionexpanded
38233          * Fires when a region is expanded.
38234          * @param {Roo.LayoutRegion} region The expanded region
38235          */
38236         "regionexpanded" : true
38237     });
38238     this.updating = false;
38239
38240     if (config.el) {
38241         this.el = Roo.get(config.el);
38242         this.initEvents();
38243     }
38244
38245 };
38246
38247 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38248
38249
38250     regions : null,
38251
38252     monitorWindowResize : true,
38253
38254
38255     updating : false,
38256
38257
38258     onRender : function(ct, position)
38259     {
38260         if(!this.el){
38261             this.el = Roo.get(ct);
38262             this.initEvents();
38263         }
38264         //this.fireEvent('render',this);
38265     },
38266
38267
38268     initEvents: function()
38269     {
38270
38271
38272         // ie scrollbar fix
38273         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38274             document.body.scroll = "no";
38275         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38276             this.el.position('relative');
38277         }
38278         this.id = this.el.id;
38279         this.el.addClass("roo-layout-container");
38280         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38281         if(this.el.dom != document.body ) {
38282             this.el.on('resize', this.layout,this);
38283             this.el.on('show', this.layout,this);
38284         }
38285
38286     },
38287
38288     /**
38289      * Returns true if this layout is currently being updated
38290      * @return {Boolean}
38291      */
38292     isUpdating : function(){
38293         return this.updating;
38294     },
38295
38296     /**
38297      * Suspend the LayoutManager from doing auto-layouts while
38298      * making multiple add or remove calls
38299      */
38300     beginUpdate : function(){
38301         this.updating = true;
38302     },
38303
38304     /**
38305      * Restore auto-layouts and optionally disable the manager from performing a layout
38306      * @param {Boolean} noLayout true to disable a layout update
38307      */
38308     endUpdate : function(noLayout){
38309         this.updating = false;
38310         if(!noLayout){
38311             this.layout();
38312         }
38313     },
38314
38315     layout: function(){
38316         // abstract...
38317     },
38318
38319     onRegionResized : function(region, newSize){
38320         this.fireEvent("regionresized", region, newSize);
38321         this.layout();
38322     },
38323
38324     onRegionCollapsed : function(region){
38325         this.fireEvent("regioncollapsed", region);
38326     },
38327
38328     onRegionExpanded : function(region){
38329         this.fireEvent("regionexpanded", region);
38330     },
38331
38332     /**
38333      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38334      * performs box-model adjustments.
38335      * @return {Object} The size as an object {width: (the width), height: (the height)}
38336      */
38337     getViewSize : function()
38338     {
38339         var size;
38340         if(this.el.dom != document.body){
38341             size = this.el.getSize();
38342         }else{
38343             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38344         }
38345         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38346         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38347         return size;
38348     },
38349
38350     /**
38351      * Returns the Element this layout is bound to.
38352      * @return {Roo.Element}
38353      */
38354     getEl : function(){
38355         return this.el;
38356     },
38357
38358     /**
38359      * Returns the specified region.
38360      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38361      * @return {Roo.LayoutRegion}
38362      */
38363     getRegion : function(target){
38364         return this.regions[target.toLowerCase()];
38365     },
38366
38367     onWindowResize : function(){
38368         if(this.monitorWindowResize){
38369             this.layout();
38370         }
38371     }
38372 });
38373 /*
38374  * Based on:
38375  * Ext JS Library 1.1.1
38376  * Copyright(c) 2006-2007, Ext JS, LLC.
38377  *
38378  * Originally Released Under LGPL - original licence link has changed is not relivant.
38379  *
38380  * Fork - LGPL
38381  * <script type="text/javascript">
38382  */
38383 /**
38384  * @class Roo.bootstrap.layout.Border
38385  * @extends Roo.bootstrap.layout.Manager
38386  * @builder-top
38387  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38388  * please see: examples/bootstrap/nested.html<br><br>
38389  
38390 <b>The container the layout is rendered into can be either the body element or any other element.
38391 If it is not the body element, the container needs to either be an absolute positioned element,
38392 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38393 the container size if it is not the body element.</b>
38394
38395 * @constructor
38396 * Create a new Border
38397 * @param {Object} config Configuration options
38398  */
38399 Roo.bootstrap.layout.Border = function(config){
38400     config = config || {};
38401     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38402     
38403     
38404     
38405     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38406         if(config[region]){
38407             config[region].region = region;
38408             this.addRegion(config[region]);
38409         }
38410     },this);
38411     
38412 };
38413
38414 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38415
38416 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38417     
38418     parent : false, // this might point to a 'nest' or a ???
38419     
38420     /**
38421      * Creates and adds a new region if it doesn't already exist.
38422      * @param {String} target The target region key (north, south, east, west or center).
38423      * @param {Object} config The regions config object
38424      * @return {BorderLayoutRegion} The new region
38425      */
38426     addRegion : function(config)
38427     {
38428         if(!this.regions[config.region]){
38429             var r = this.factory(config);
38430             this.bindRegion(r);
38431         }
38432         return this.regions[config.region];
38433     },
38434
38435     // private (kinda)
38436     bindRegion : function(r){
38437         this.regions[r.config.region] = r;
38438         
38439         r.on("visibilitychange",    this.layout, this);
38440         r.on("paneladded",          this.layout, this);
38441         r.on("panelremoved",        this.layout, this);
38442         r.on("invalidated",         this.layout, this);
38443         r.on("resized",             this.onRegionResized, this);
38444         r.on("collapsed",           this.onRegionCollapsed, this);
38445         r.on("expanded",            this.onRegionExpanded, this);
38446     },
38447
38448     /**
38449      * Performs a layout update.
38450      */
38451     layout : function()
38452     {
38453         if(this.updating) {
38454             return;
38455         }
38456         
38457         // render all the rebions if they have not been done alreayd?
38458         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38459             if(this.regions[region] && !this.regions[region].bodyEl){
38460                 this.regions[region].onRender(this.el)
38461             }
38462         },this);
38463         
38464         var size = this.getViewSize();
38465         var w = size.width;
38466         var h = size.height;
38467         var centerW = w;
38468         var centerH = h;
38469         var centerY = 0;
38470         var centerX = 0;
38471         //var x = 0, y = 0;
38472
38473         var rs = this.regions;
38474         var north = rs["north"];
38475         var south = rs["south"]; 
38476         var west = rs["west"];
38477         var east = rs["east"];
38478         var center = rs["center"];
38479         //if(this.hideOnLayout){ // not supported anymore
38480             //c.el.setStyle("display", "none");
38481         //}
38482         if(north && north.isVisible()){
38483             var b = north.getBox();
38484             var m = north.getMargins();
38485             b.width = w - (m.left+m.right);
38486             b.x = m.left;
38487             b.y = m.top;
38488             centerY = b.height + b.y + m.bottom;
38489             centerH -= centerY;
38490             north.updateBox(this.safeBox(b));
38491         }
38492         if(south && south.isVisible()){
38493             var b = south.getBox();
38494             var m = south.getMargins();
38495             b.width = w - (m.left+m.right);
38496             b.x = m.left;
38497             var totalHeight = (b.height + m.top + m.bottom);
38498             b.y = h - totalHeight + m.top;
38499             centerH -= totalHeight;
38500             south.updateBox(this.safeBox(b));
38501         }
38502         if(west && west.isVisible()){
38503             var b = west.getBox();
38504             var m = west.getMargins();
38505             b.height = centerH - (m.top+m.bottom);
38506             b.x = m.left;
38507             b.y = centerY + m.top;
38508             var totalWidth = (b.width + m.left + m.right);
38509             centerX += totalWidth;
38510             centerW -= totalWidth;
38511             west.updateBox(this.safeBox(b));
38512         }
38513         if(east && east.isVisible()){
38514             var b = east.getBox();
38515             var m = east.getMargins();
38516             b.height = centerH - (m.top+m.bottom);
38517             var totalWidth = (b.width + m.left + m.right);
38518             b.x = w - totalWidth + m.left;
38519             b.y = centerY + m.top;
38520             centerW -= totalWidth;
38521             east.updateBox(this.safeBox(b));
38522         }
38523         if(center){
38524             var m = center.getMargins();
38525             var centerBox = {
38526                 x: centerX + m.left,
38527                 y: centerY + m.top,
38528                 width: centerW - (m.left+m.right),
38529                 height: centerH - (m.top+m.bottom)
38530             };
38531             //if(this.hideOnLayout){
38532                 //center.el.setStyle("display", "block");
38533             //}
38534             center.updateBox(this.safeBox(centerBox));
38535         }
38536         this.el.repaint();
38537         this.fireEvent("layout", this);
38538     },
38539
38540     // private
38541     safeBox : function(box){
38542         box.width = Math.max(0, box.width);
38543         box.height = Math.max(0, box.height);
38544         return box;
38545     },
38546
38547     /**
38548      * Adds a ContentPanel (or subclass) to this layout.
38549      * @param {String} target The target region key (north, south, east, west or center).
38550      * @param {Roo.ContentPanel} panel The panel to add
38551      * @return {Roo.ContentPanel} The added panel
38552      */
38553     add : function(target, panel){
38554          
38555         target = target.toLowerCase();
38556         return this.regions[target].add(panel);
38557     },
38558
38559     /**
38560      * Remove a ContentPanel (or subclass) to this layout.
38561      * @param {String} target The target region key (north, south, east, west or center).
38562      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38563      * @return {Roo.ContentPanel} The removed panel
38564      */
38565     remove : function(target, panel){
38566         target = target.toLowerCase();
38567         return this.regions[target].remove(panel);
38568     },
38569
38570     /**
38571      * Searches all regions for a panel with the specified id
38572      * @param {String} panelId
38573      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38574      */
38575     findPanel : function(panelId){
38576         var rs = this.regions;
38577         for(var target in rs){
38578             if(typeof rs[target] != "function"){
38579                 var p = rs[target].getPanel(panelId);
38580                 if(p){
38581                     return p;
38582                 }
38583             }
38584         }
38585         return null;
38586     },
38587
38588     /**
38589      * Searches all regions for a panel with the specified id and activates (shows) it.
38590      * @param {String/ContentPanel} panelId The panels id or the panel itself
38591      * @return {Roo.ContentPanel} The shown panel or null
38592      */
38593     showPanel : function(panelId) {
38594       var rs = this.regions;
38595       for(var target in rs){
38596          var r = rs[target];
38597          if(typeof r != "function"){
38598             if(r.hasPanel(panelId)){
38599                return r.showPanel(panelId);
38600             }
38601          }
38602       }
38603       return null;
38604    },
38605
38606    /**
38607      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38608      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38609      */
38610    /*
38611     restoreState : function(provider){
38612         if(!provider){
38613             provider = Roo.state.Manager;
38614         }
38615         var sm = new Roo.LayoutStateManager();
38616         sm.init(this, provider);
38617     },
38618 */
38619  
38620  
38621     /**
38622      * Adds a xtype elements to the layout.
38623      * <pre><code>
38624
38625 layout.addxtype({
38626        xtype : 'ContentPanel',
38627        region: 'west',
38628        items: [ .... ]
38629    }
38630 );
38631
38632 layout.addxtype({
38633         xtype : 'NestedLayoutPanel',
38634         region: 'west',
38635         layout: {
38636            center: { },
38637            west: { }   
38638         },
38639         items : [ ... list of content panels or nested layout panels.. ]
38640    }
38641 );
38642 </code></pre>
38643      * @param {Object} cfg Xtype definition of item to add.
38644      */
38645     addxtype : function(cfg)
38646     {
38647         // basically accepts a pannel...
38648         // can accept a layout region..!?!?
38649         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38650         
38651         
38652         // theory?  children can only be panels??
38653         
38654         //if (!cfg.xtype.match(/Panel$/)) {
38655         //    return false;
38656         //}
38657         var ret = false;
38658         
38659         if (typeof(cfg.region) == 'undefined') {
38660             Roo.log("Failed to add Panel, region was not set");
38661             Roo.log(cfg);
38662             return false;
38663         }
38664         var region = cfg.region;
38665         delete cfg.region;
38666         
38667           
38668         var xitems = [];
38669         if (cfg.items) {
38670             xitems = cfg.items;
38671             delete cfg.items;
38672         }
38673         var nb = false;
38674         
38675         if ( region == 'center') {
38676             Roo.log("Center: " + cfg.title);
38677         }
38678         
38679         
38680         switch(cfg.xtype) 
38681         {
38682             case 'Content':  // ContentPanel (el, cfg)
38683             case 'Scroll':  // ContentPanel (el, cfg)
38684             case 'View': 
38685                 cfg.autoCreate = cfg.autoCreate || true;
38686                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38687                 //} else {
38688                 //    var el = this.el.createChild();
38689                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38690                 //}
38691                 
38692                 this.add(region, ret);
38693                 break;
38694             
38695             /*
38696             case 'TreePanel': // our new panel!
38697                 cfg.el = this.el.createChild();
38698                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38699                 this.add(region, ret);
38700                 break;
38701             */
38702             
38703             case 'Nest': 
38704                 // create a new Layout (which is  a Border Layout...
38705                 
38706                 var clayout = cfg.layout;
38707                 clayout.el  = this.el.createChild();
38708                 clayout.items   = clayout.items  || [];
38709                 
38710                 delete cfg.layout;
38711                 
38712                 // replace this exitems with the clayout ones..
38713                 xitems = clayout.items;
38714                  
38715                 // force background off if it's in center...
38716                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38717                     cfg.background = false;
38718                 }
38719                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38720                 
38721                 
38722                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38723                 //console.log('adding nested layout panel '  + cfg.toSource());
38724                 this.add(region, ret);
38725                 nb = {}; /// find first...
38726                 break;
38727             
38728             case 'Grid':
38729                 
38730                 // needs grid and region
38731                 
38732                 //var el = this.getRegion(region).el.createChild();
38733                 /*
38734                  *var el = this.el.createChild();
38735                 // create the grid first...
38736                 cfg.grid.container = el;
38737                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38738                 */
38739                 
38740                 if (region == 'center' && this.active ) {
38741                     cfg.background = false;
38742                 }
38743                 
38744                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38745                 
38746                 this.add(region, ret);
38747                 /*
38748                 if (cfg.background) {
38749                     // render grid on panel activation (if panel background)
38750                     ret.on('activate', function(gp) {
38751                         if (!gp.grid.rendered) {
38752                     //        gp.grid.render(el);
38753                         }
38754                     });
38755                 } else {
38756                   //  cfg.grid.render(el);
38757                 }
38758                 */
38759                 break;
38760            
38761            
38762             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38763                 // it was the old xcomponent building that caused this before.
38764                 // espeically if border is the top element in the tree.
38765                 ret = this;
38766                 break; 
38767                 
38768                     
38769                 
38770                 
38771                 
38772             default:
38773                 /*
38774                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38775                     
38776                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38777                     this.add(region, ret);
38778                 } else {
38779                 */
38780                     Roo.log(cfg);
38781                     throw "Can not add '" + cfg.xtype + "' to Border";
38782                     return null;
38783              
38784                                 
38785              
38786         }
38787         this.beginUpdate();
38788         // add children..
38789         var region = '';
38790         var abn = {};
38791         Roo.each(xitems, function(i)  {
38792             region = nb && i.region ? i.region : false;
38793             
38794             var add = ret.addxtype(i);
38795            
38796             if (region) {
38797                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38798                 if (!i.background) {
38799                     abn[region] = nb[region] ;
38800                 }
38801             }
38802             
38803         });
38804         this.endUpdate();
38805
38806         // make the last non-background panel active..
38807         //if (nb) { Roo.log(abn); }
38808         if (nb) {
38809             
38810             for(var r in abn) {
38811                 region = this.getRegion(r);
38812                 if (region) {
38813                     // tried using nb[r], but it does not work..
38814                      
38815                     region.showPanel(abn[r]);
38816                    
38817                 }
38818             }
38819         }
38820         return ret;
38821         
38822     },
38823     
38824     
38825 // private
38826     factory : function(cfg)
38827     {
38828         
38829         var validRegions = Roo.bootstrap.layout.Border.regions;
38830
38831         var target = cfg.region;
38832         cfg.mgr = this;
38833         
38834         var r = Roo.bootstrap.layout;
38835         Roo.log(target);
38836         switch(target){
38837             case "north":
38838                 return new r.North(cfg);
38839             case "south":
38840                 return new r.South(cfg);
38841             case "east":
38842                 return new r.East(cfg);
38843             case "west":
38844                 return new r.West(cfg);
38845             case "center":
38846                 return new r.Center(cfg);
38847         }
38848         throw 'Layout region "'+target+'" not supported.';
38849     }
38850     
38851     
38852 });
38853  /*
38854  * Based on:
38855  * Ext JS Library 1.1.1
38856  * Copyright(c) 2006-2007, Ext JS, LLC.
38857  *
38858  * Originally Released Under LGPL - original licence link has changed is not relivant.
38859  *
38860  * Fork - LGPL
38861  * <script type="text/javascript">
38862  */
38863  
38864 /**
38865  * @class Roo.bootstrap.layout.Basic
38866  * @extends Roo.util.Observable
38867  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38868  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38869  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38870  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38871  * @cfg {string}   region  the region that it inhabits..
38872  * @cfg {bool}   skipConfig skip config?
38873  * 
38874
38875  */
38876 Roo.bootstrap.layout.Basic = function(config){
38877     
38878     this.mgr = config.mgr;
38879     
38880     this.position = config.region;
38881     
38882     var skipConfig = config.skipConfig;
38883     
38884     this.events = {
38885         /**
38886          * @scope Roo.BasicLayoutRegion
38887          */
38888         
38889         /**
38890          * @event beforeremove
38891          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38892          * @param {Roo.LayoutRegion} this
38893          * @param {Roo.ContentPanel} panel The panel
38894          * @param {Object} e The cancel event object
38895          */
38896         "beforeremove" : true,
38897         /**
38898          * @event invalidated
38899          * Fires when the layout for this region is changed.
38900          * @param {Roo.LayoutRegion} this
38901          */
38902         "invalidated" : true,
38903         /**
38904          * @event visibilitychange
38905          * Fires when this region is shown or hidden 
38906          * @param {Roo.LayoutRegion} this
38907          * @param {Boolean} visibility true or false
38908          */
38909         "visibilitychange" : true,
38910         /**
38911          * @event paneladded
38912          * Fires when a panel is added. 
38913          * @param {Roo.LayoutRegion} this
38914          * @param {Roo.ContentPanel} panel The panel
38915          */
38916         "paneladded" : true,
38917         /**
38918          * @event panelremoved
38919          * Fires when a panel is removed. 
38920          * @param {Roo.LayoutRegion} this
38921          * @param {Roo.ContentPanel} panel The panel
38922          */
38923         "panelremoved" : true,
38924         /**
38925          * @event beforecollapse
38926          * Fires when this region before collapse.
38927          * @param {Roo.LayoutRegion} this
38928          */
38929         "beforecollapse" : true,
38930         /**
38931          * @event collapsed
38932          * Fires when this region is collapsed.
38933          * @param {Roo.LayoutRegion} this
38934          */
38935         "collapsed" : true,
38936         /**
38937          * @event expanded
38938          * Fires when this region is expanded.
38939          * @param {Roo.LayoutRegion} this
38940          */
38941         "expanded" : true,
38942         /**
38943          * @event slideshow
38944          * Fires when this region is slid into view.
38945          * @param {Roo.LayoutRegion} this
38946          */
38947         "slideshow" : true,
38948         /**
38949          * @event slidehide
38950          * Fires when this region slides out of view. 
38951          * @param {Roo.LayoutRegion} this
38952          */
38953         "slidehide" : true,
38954         /**
38955          * @event panelactivated
38956          * Fires when a panel is activated. 
38957          * @param {Roo.LayoutRegion} this
38958          * @param {Roo.ContentPanel} panel The activated panel
38959          */
38960         "panelactivated" : true,
38961         /**
38962          * @event resized
38963          * Fires when the user resizes this region. 
38964          * @param {Roo.LayoutRegion} this
38965          * @param {Number} newSize The new size (width for east/west, height for north/south)
38966          */
38967         "resized" : true
38968     };
38969     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38970     this.panels = new Roo.util.MixedCollection();
38971     this.panels.getKey = this.getPanelId.createDelegate(this);
38972     this.box = null;
38973     this.activePanel = null;
38974     // ensure listeners are added...
38975     
38976     if (config.listeners || config.events) {
38977         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38978             listeners : config.listeners || {},
38979             events : config.events || {}
38980         });
38981     }
38982     
38983     if(skipConfig !== true){
38984         this.applyConfig(config);
38985     }
38986 };
38987
38988 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38989 {
38990     getPanelId : function(p){
38991         return p.getId();
38992     },
38993     
38994     applyConfig : function(config){
38995         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38996         this.config = config;
38997         
38998     },
38999     
39000     /**
39001      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
39002      * the width, for horizontal (north, south) the height.
39003      * @param {Number} newSize The new width or height
39004      */
39005     resizeTo : function(newSize){
39006         var el = this.el ? this.el :
39007                  (this.activePanel ? this.activePanel.getEl() : null);
39008         if(el){
39009             switch(this.position){
39010                 case "east":
39011                 case "west":
39012                     el.setWidth(newSize);
39013                     this.fireEvent("resized", this, newSize);
39014                 break;
39015                 case "north":
39016                 case "south":
39017                     el.setHeight(newSize);
39018                     this.fireEvent("resized", this, newSize);
39019                 break;                
39020             }
39021         }
39022     },
39023     
39024     getBox : function(){
39025         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39026     },
39027     
39028     getMargins : function(){
39029         return this.margins;
39030     },
39031     
39032     updateBox : function(box){
39033         this.box = box;
39034         var el = this.activePanel.getEl();
39035         el.dom.style.left = box.x + "px";
39036         el.dom.style.top = box.y + "px";
39037         this.activePanel.setSize(box.width, box.height);
39038     },
39039     
39040     /**
39041      * Returns the container element for this region.
39042      * @return {Roo.Element}
39043      */
39044     getEl : function(){
39045         return this.activePanel;
39046     },
39047     
39048     /**
39049      * Returns true if this region is currently visible.
39050      * @return {Boolean}
39051      */
39052     isVisible : function(){
39053         return this.activePanel ? true : false;
39054     },
39055     
39056     setActivePanel : function(panel){
39057         panel = this.getPanel(panel);
39058         if(this.activePanel && this.activePanel != panel){
39059             this.activePanel.setActiveState(false);
39060             this.activePanel.getEl().setLeftTop(-10000,-10000);
39061         }
39062         this.activePanel = panel;
39063         panel.setActiveState(true);
39064         if(this.box){
39065             panel.setSize(this.box.width, this.box.height);
39066         }
39067         this.fireEvent("panelactivated", this, panel);
39068         this.fireEvent("invalidated");
39069     },
39070     
39071     /**
39072      * Show the specified panel.
39073      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39074      * @return {Roo.ContentPanel} The shown panel or null
39075      */
39076     showPanel : function(panel){
39077         panel = this.getPanel(panel);
39078         if(panel){
39079             this.setActivePanel(panel);
39080         }
39081         return panel;
39082     },
39083     
39084     /**
39085      * Get the active panel for this region.
39086      * @return {Roo.ContentPanel} The active panel or null
39087      */
39088     getActivePanel : function(){
39089         return this.activePanel;
39090     },
39091     
39092     /**
39093      * Add the passed ContentPanel(s)
39094      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39095      * @return {Roo.ContentPanel} The panel added (if only one was added)
39096      */
39097     add : function(panel){
39098         if(arguments.length > 1){
39099             for(var i = 0, len = arguments.length; i < len; i++) {
39100                 this.add(arguments[i]);
39101             }
39102             return null;
39103         }
39104         if(this.hasPanel(panel)){
39105             this.showPanel(panel);
39106             return panel;
39107         }
39108         var el = panel.getEl();
39109         if(el.dom.parentNode != this.mgr.el.dom){
39110             this.mgr.el.dom.appendChild(el.dom);
39111         }
39112         if(panel.setRegion){
39113             panel.setRegion(this);
39114         }
39115         this.panels.add(panel);
39116         el.setStyle("position", "absolute");
39117         if(!panel.background){
39118             this.setActivePanel(panel);
39119             if(this.config.initialSize && this.panels.getCount()==1){
39120                 this.resizeTo(this.config.initialSize);
39121             }
39122         }
39123         this.fireEvent("paneladded", this, panel);
39124         return panel;
39125     },
39126     
39127     /**
39128      * Returns true if the panel is in this region.
39129      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39130      * @return {Boolean}
39131      */
39132     hasPanel : function(panel){
39133         if(typeof panel == "object"){ // must be panel obj
39134             panel = panel.getId();
39135         }
39136         return this.getPanel(panel) ? true : false;
39137     },
39138     
39139     /**
39140      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39141      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39142      * @param {Boolean} preservePanel Overrides the config preservePanel option
39143      * @return {Roo.ContentPanel} The panel that was removed
39144      */
39145     remove : function(panel, preservePanel){
39146         panel = this.getPanel(panel);
39147         if(!panel){
39148             return null;
39149         }
39150         var e = {};
39151         this.fireEvent("beforeremove", this, panel, e);
39152         if(e.cancel === true){
39153             return null;
39154         }
39155         var panelId = panel.getId();
39156         this.panels.removeKey(panelId);
39157         return panel;
39158     },
39159     
39160     /**
39161      * Returns the panel specified or null if it's not in this region.
39162      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39163      * @return {Roo.ContentPanel}
39164      */
39165     getPanel : function(id){
39166         if(typeof id == "object"){ // must be panel obj
39167             return id;
39168         }
39169         return this.panels.get(id);
39170     },
39171     
39172     /**
39173      * Returns this regions position (north/south/east/west/center).
39174      * @return {String} 
39175      */
39176     getPosition: function(){
39177         return this.position;    
39178     }
39179 });/*
39180  * Based on:
39181  * Ext JS Library 1.1.1
39182  * Copyright(c) 2006-2007, Ext JS, LLC.
39183  *
39184  * Originally Released Under LGPL - original licence link has changed is not relivant.
39185  *
39186  * Fork - LGPL
39187  * <script type="text/javascript">
39188  */
39189  
39190 /**
39191  * @class Roo.bootstrap.layout.Region
39192  * @extends Roo.bootstrap.layout.Basic
39193  * This class represents a region in a layout manager.
39194  
39195  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39196  * @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})
39197  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39198  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39199  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39200  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39201  * @cfg {String}    title           The title for the region (overrides panel titles)
39202  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39203  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39204  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39205  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39206  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39207  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39208  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39209  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39210  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39211  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39212
39213  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39214  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39215  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39216  * @cfg {Number}    width           For East/West panels
39217  * @cfg {Number}    height          For North/South panels
39218  * @cfg {Boolean}   split           To show the splitter
39219  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39220  * 
39221  * @cfg {string}   cls             Extra CSS classes to add to region
39222  * 
39223  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39224  * @cfg {string}   region  the region that it inhabits..
39225  *
39226
39227  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39228  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39229
39230  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39231  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39232  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39233  */
39234 Roo.bootstrap.layout.Region = function(config)
39235 {
39236     this.applyConfig(config);
39237
39238     var mgr = config.mgr;
39239     var pos = config.region;
39240     config.skipConfig = true;
39241     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39242     
39243     if (mgr.el) {
39244         this.onRender(mgr.el);   
39245     }
39246      
39247     this.visible = true;
39248     this.collapsed = false;
39249     this.unrendered_panels = [];
39250 };
39251
39252 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39253
39254     position: '', // set by wrapper (eg. north/south etc..)
39255     unrendered_panels : null,  // unrendered panels.
39256     
39257     tabPosition : false,
39258     
39259     mgr: false, // points to 'Border'
39260     
39261     
39262     createBody : function(){
39263         /** This region's body element 
39264         * @type Roo.Element */
39265         this.bodyEl = this.el.createChild({
39266                 tag: "div",
39267                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39268         });
39269     },
39270
39271     onRender: function(ctr, pos)
39272     {
39273         var dh = Roo.DomHelper;
39274         /** This region's container element 
39275         * @type Roo.Element */
39276         this.el = dh.append(ctr.dom, {
39277                 tag: "div",
39278                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39279             }, true);
39280         /** This region's title element 
39281         * @type Roo.Element */
39282     
39283         this.titleEl = dh.append(this.el.dom,  {
39284                 tag: "div",
39285                 unselectable: "on",
39286                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39287                 children:[
39288                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39289                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39290                 ]
39291             }, true);
39292         
39293         this.titleEl.enableDisplayMode();
39294         /** This region's title text element 
39295         * @type HTMLElement */
39296         this.titleTextEl = this.titleEl.dom.firstChild;
39297         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39298         /*
39299         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39300         this.closeBtn.enableDisplayMode();
39301         this.closeBtn.on("click", this.closeClicked, this);
39302         this.closeBtn.hide();
39303     */
39304         this.createBody(this.config);
39305         if(this.config.hideWhenEmpty){
39306             this.hide();
39307             this.on("paneladded", this.validateVisibility, this);
39308             this.on("panelremoved", this.validateVisibility, this);
39309         }
39310         if(this.autoScroll){
39311             this.bodyEl.setStyle("overflow", "auto");
39312         }else{
39313             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39314         }
39315         //if(c.titlebar !== false){
39316             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39317                 this.titleEl.hide();
39318             }else{
39319                 this.titleEl.show();
39320                 if(this.config.title){
39321                     this.titleTextEl.innerHTML = this.config.title;
39322                 }
39323             }
39324         //}
39325         if(this.config.collapsed){
39326             this.collapse(true);
39327         }
39328         if(this.config.hidden){
39329             this.hide();
39330         }
39331         
39332         if (this.unrendered_panels && this.unrendered_panels.length) {
39333             for (var i =0;i< this.unrendered_panels.length; i++) {
39334                 this.add(this.unrendered_panels[i]);
39335             }
39336             this.unrendered_panels = null;
39337             
39338         }
39339         
39340     },
39341     
39342     applyConfig : function(c)
39343     {
39344         /*
39345          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39346             var dh = Roo.DomHelper;
39347             if(c.titlebar !== false){
39348                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39349                 this.collapseBtn.on("click", this.collapse, this);
39350                 this.collapseBtn.enableDisplayMode();
39351                 /*
39352                 if(c.showPin === true || this.showPin){
39353                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39354                     this.stickBtn.enableDisplayMode();
39355                     this.stickBtn.on("click", this.expand, this);
39356                     this.stickBtn.hide();
39357                 }
39358                 
39359             }
39360             */
39361             /** This region's collapsed element
39362             * @type Roo.Element */
39363             /*
39364              *
39365             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39366                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39367             ]}, true);
39368             
39369             if(c.floatable !== false){
39370                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39371                this.collapsedEl.on("click", this.collapseClick, this);
39372             }
39373
39374             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39375                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39376                    id: "message", unselectable: "on", style:{"float":"left"}});
39377                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39378              }
39379             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39380             this.expandBtn.on("click", this.expand, this);
39381             
39382         }
39383         
39384         if(this.collapseBtn){
39385             this.collapseBtn.setVisible(c.collapsible == true);
39386         }
39387         
39388         this.cmargins = c.cmargins || this.cmargins ||
39389                          (this.position == "west" || this.position == "east" ?
39390                              {top: 0, left: 2, right:2, bottom: 0} :
39391                              {top: 2, left: 0, right:0, bottom: 2});
39392         */
39393         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39394         
39395         
39396         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39397         
39398         this.autoScroll = c.autoScroll || false;
39399         
39400         
39401        
39402         
39403         this.duration = c.duration || .30;
39404         this.slideDuration = c.slideDuration || .45;
39405         this.config = c;
39406        
39407     },
39408     /**
39409      * Returns true if this region is currently visible.
39410      * @return {Boolean}
39411      */
39412     isVisible : function(){
39413         return this.visible;
39414     },
39415
39416     /**
39417      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39418      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39419      */
39420     //setCollapsedTitle : function(title){
39421     //    title = title || "&#160;";
39422      //   if(this.collapsedTitleTextEl){
39423       //      this.collapsedTitleTextEl.innerHTML = title;
39424        // }
39425     //},
39426
39427     getBox : function(){
39428         var b;
39429       //  if(!this.collapsed){
39430             b = this.el.getBox(false, true);
39431        // }else{
39432           //  b = this.collapsedEl.getBox(false, true);
39433         //}
39434         return b;
39435     },
39436
39437     getMargins : function(){
39438         return this.margins;
39439         //return this.collapsed ? this.cmargins : this.margins;
39440     },
39441 /*
39442     highlight : function(){
39443         this.el.addClass("x-layout-panel-dragover");
39444     },
39445
39446     unhighlight : function(){
39447         this.el.removeClass("x-layout-panel-dragover");
39448     },
39449 */
39450     updateBox : function(box)
39451     {
39452         if (!this.bodyEl) {
39453             return; // not rendered yet..
39454         }
39455         
39456         this.box = box;
39457         if(!this.collapsed){
39458             this.el.dom.style.left = box.x + "px";
39459             this.el.dom.style.top = box.y + "px";
39460             this.updateBody(box.width, box.height);
39461         }else{
39462             this.collapsedEl.dom.style.left = box.x + "px";
39463             this.collapsedEl.dom.style.top = box.y + "px";
39464             this.collapsedEl.setSize(box.width, box.height);
39465         }
39466         if(this.tabs){
39467             this.tabs.autoSizeTabs();
39468         }
39469     },
39470
39471     updateBody : function(w, h)
39472     {
39473         if(w !== null){
39474             this.el.setWidth(w);
39475             w -= this.el.getBorderWidth("rl");
39476             if(this.config.adjustments){
39477                 w += this.config.adjustments[0];
39478             }
39479         }
39480         if(h !== null && h > 0){
39481             this.el.setHeight(h);
39482             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39483             h -= this.el.getBorderWidth("tb");
39484             if(this.config.adjustments){
39485                 h += this.config.adjustments[1];
39486             }
39487             this.bodyEl.setHeight(h);
39488             if(this.tabs){
39489                 h = this.tabs.syncHeight(h);
39490             }
39491         }
39492         if(this.panelSize){
39493             w = w !== null ? w : this.panelSize.width;
39494             h = h !== null ? h : this.panelSize.height;
39495         }
39496         if(this.activePanel){
39497             var el = this.activePanel.getEl();
39498             w = w !== null ? w : el.getWidth();
39499             h = h !== null ? h : el.getHeight();
39500             this.panelSize = {width: w, height: h};
39501             this.activePanel.setSize(w, h);
39502         }
39503         if(Roo.isIE && this.tabs){
39504             this.tabs.el.repaint();
39505         }
39506     },
39507
39508     /**
39509      * Returns the container element for this region.
39510      * @return {Roo.Element}
39511      */
39512     getEl : function(){
39513         return this.el;
39514     },
39515
39516     /**
39517      * Hides this region.
39518      */
39519     hide : function(){
39520         //if(!this.collapsed){
39521             this.el.dom.style.left = "-2000px";
39522             this.el.hide();
39523         //}else{
39524          //   this.collapsedEl.dom.style.left = "-2000px";
39525          //   this.collapsedEl.hide();
39526        // }
39527         this.visible = false;
39528         this.fireEvent("visibilitychange", this, false);
39529     },
39530
39531     /**
39532      * Shows this region if it was previously hidden.
39533      */
39534     show : function(){
39535         //if(!this.collapsed){
39536             this.el.show();
39537         //}else{
39538         //    this.collapsedEl.show();
39539        // }
39540         this.visible = true;
39541         this.fireEvent("visibilitychange", this, true);
39542     },
39543 /*
39544     closeClicked : function(){
39545         if(this.activePanel){
39546             this.remove(this.activePanel);
39547         }
39548     },
39549
39550     collapseClick : function(e){
39551         if(this.isSlid){
39552            e.stopPropagation();
39553            this.slideIn();
39554         }else{
39555            e.stopPropagation();
39556            this.slideOut();
39557         }
39558     },
39559 */
39560     /**
39561      * Collapses this region.
39562      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39563      */
39564     /*
39565     collapse : function(skipAnim, skipCheck = false){
39566         if(this.collapsed) {
39567             return;
39568         }
39569         
39570         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39571             
39572             this.collapsed = true;
39573             if(this.split){
39574                 this.split.el.hide();
39575             }
39576             if(this.config.animate && skipAnim !== true){
39577                 this.fireEvent("invalidated", this);
39578                 this.animateCollapse();
39579             }else{
39580                 this.el.setLocation(-20000,-20000);
39581                 this.el.hide();
39582                 this.collapsedEl.show();
39583                 this.fireEvent("collapsed", this);
39584                 this.fireEvent("invalidated", this);
39585             }
39586         }
39587         
39588     },
39589 */
39590     animateCollapse : function(){
39591         // overridden
39592     },
39593
39594     /**
39595      * Expands this region if it was previously collapsed.
39596      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39597      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39598      */
39599     /*
39600     expand : function(e, skipAnim){
39601         if(e) {
39602             e.stopPropagation();
39603         }
39604         if(!this.collapsed || this.el.hasActiveFx()) {
39605             return;
39606         }
39607         if(this.isSlid){
39608             this.afterSlideIn();
39609             skipAnim = true;
39610         }
39611         this.collapsed = false;
39612         if(this.config.animate && skipAnim !== true){
39613             this.animateExpand();
39614         }else{
39615             this.el.show();
39616             if(this.split){
39617                 this.split.el.show();
39618             }
39619             this.collapsedEl.setLocation(-2000,-2000);
39620             this.collapsedEl.hide();
39621             this.fireEvent("invalidated", this);
39622             this.fireEvent("expanded", this);
39623         }
39624     },
39625 */
39626     animateExpand : function(){
39627         // overridden
39628     },
39629
39630     initTabs : function()
39631     {
39632         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39633         
39634         var ts = new Roo.bootstrap.panel.Tabs({
39635             el: this.bodyEl.dom,
39636             region : this,
39637             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39638             disableTooltips: this.config.disableTabTips,
39639             toolbar : this.config.toolbar
39640         });
39641         
39642         if(this.config.hideTabs){
39643             ts.stripWrap.setDisplayed(false);
39644         }
39645         this.tabs = ts;
39646         ts.resizeTabs = this.config.resizeTabs === true;
39647         ts.minTabWidth = this.config.minTabWidth || 40;
39648         ts.maxTabWidth = this.config.maxTabWidth || 250;
39649         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39650         ts.monitorResize = false;
39651         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39652         ts.bodyEl.addClass('roo-layout-tabs-body');
39653         this.panels.each(this.initPanelAsTab, this);
39654     },
39655
39656     initPanelAsTab : function(panel){
39657         var ti = this.tabs.addTab(
39658             panel.getEl().id,
39659             panel.getTitle(),
39660             null,
39661             this.config.closeOnTab && panel.isClosable(),
39662             panel.tpl
39663         );
39664         if(panel.tabTip !== undefined){
39665             ti.setTooltip(panel.tabTip);
39666         }
39667         ti.on("activate", function(){
39668               this.setActivePanel(panel);
39669         }, this);
39670         
39671         if(this.config.closeOnTab){
39672             ti.on("beforeclose", function(t, e){
39673                 e.cancel = true;
39674                 this.remove(panel);
39675             }, this);
39676         }
39677         
39678         panel.tabItem = ti;
39679         
39680         return ti;
39681     },
39682
39683     updatePanelTitle : function(panel, title)
39684     {
39685         if(this.activePanel == panel){
39686             this.updateTitle(title);
39687         }
39688         if(this.tabs){
39689             var ti = this.tabs.getTab(panel.getEl().id);
39690             ti.setText(title);
39691             if(panel.tabTip !== undefined){
39692                 ti.setTooltip(panel.tabTip);
39693             }
39694         }
39695     },
39696
39697     updateTitle : function(title){
39698         if(this.titleTextEl && !this.config.title){
39699             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39700         }
39701     },
39702
39703     setActivePanel : function(panel)
39704     {
39705         panel = this.getPanel(panel);
39706         if(this.activePanel && this.activePanel != panel){
39707             if(this.activePanel.setActiveState(false) === false){
39708                 return;
39709             }
39710         }
39711         this.activePanel = panel;
39712         panel.setActiveState(true);
39713         if(this.panelSize){
39714             panel.setSize(this.panelSize.width, this.panelSize.height);
39715         }
39716         if(this.closeBtn){
39717             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39718         }
39719         this.updateTitle(panel.getTitle());
39720         if(this.tabs){
39721             this.fireEvent("invalidated", this);
39722         }
39723         this.fireEvent("panelactivated", this, panel);
39724     },
39725
39726     /**
39727      * Shows the specified panel.
39728      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39729      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39730      */
39731     showPanel : function(panel)
39732     {
39733         panel = this.getPanel(panel);
39734         if(panel){
39735             if(this.tabs){
39736                 var tab = this.tabs.getTab(panel.getEl().id);
39737                 if(tab.isHidden()){
39738                     this.tabs.unhideTab(tab.id);
39739                 }
39740                 tab.activate();
39741             }else{
39742                 this.setActivePanel(panel);
39743             }
39744         }
39745         return panel;
39746     },
39747
39748     /**
39749      * Get the active panel for this region.
39750      * @return {Roo.ContentPanel} The active panel or null
39751      */
39752     getActivePanel : function(){
39753         return this.activePanel;
39754     },
39755
39756     validateVisibility : function(){
39757         if(this.panels.getCount() < 1){
39758             this.updateTitle("&#160;");
39759             this.closeBtn.hide();
39760             this.hide();
39761         }else{
39762             if(!this.isVisible()){
39763                 this.show();
39764             }
39765         }
39766     },
39767
39768     /**
39769      * Adds the passed ContentPanel(s) to this region.
39770      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39771      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39772      */
39773     add : function(panel)
39774     {
39775         if(arguments.length > 1){
39776             for(var i = 0, len = arguments.length; i < len; i++) {
39777                 this.add(arguments[i]);
39778             }
39779             return null;
39780         }
39781         
39782         // if we have not been rendered yet, then we can not really do much of this..
39783         if (!this.bodyEl) {
39784             this.unrendered_panels.push(panel);
39785             return panel;
39786         }
39787         
39788         
39789         
39790         
39791         if(this.hasPanel(panel)){
39792             this.showPanel(panel);
39793             return panel;
39794         }
39795         panel.setRegion(this);
39796         this.panels.add(panel);
39797        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39798             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39799             // and hide them... ???
39800             this.bodyEl.dom.appendChild(panel.getEl().dom);
39801             if(panel.background !== true){
39802                 this.setActivePanel(panel);
39803             }
39804             this.fireEvent("paneladded", this, panel);
39805             return panel;
39806         }
39807         */
39808         if(!this.tabs){
39809             this.initTabs();
39810         }else{
39811             this.initPanelAsTab(panel);
39812         }
39813         
39814         
39815         if(panel.background !== true){
39816             this.tabs.activate(panel.getEl().id);
39817         }
39818         this.fireEvent("paneladded", this, panel);
39819         return panel;
39820     },
39821
39822     /**
39823      * Hides the tab for the specified panel.
39824      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39825      */
39826     hidePanel : function(panel){
39827         if(this.tabs && (panel = this.getPanel(panel))){
39828             this.tabs.hideTab(panel.getEl().id);
39829         }
39830     },
39831
39832     /**
39833      * Unhides the tab for a previously hidden panel.
39834      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39835      */
39836     unhidePanel : function(panel){
39837         if(this.tabs && (panel = this.getPanel(panel))){
39838             this.tabs.unhideTab(panel.getEl().id);
39839         }
39840     },
39841
39842     clearPanels : function(){
39843         while(this.panels.getCount() > 0){
39844              this.remove(this.panels.first());
39845         }
39846     },
39847
39848     /**
39849      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39850      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39851      * @param {Boolean} preservePanel Overrides the config preservePanel option
39852      * @return {Roo.ContentPanel} The panel that was removed
39853      */
39854     remove : function(panel, preservePanel)
39855     {
39856         panel = this.getPanel(panel);
39857         if(!panel){
39858             return null;
39859         }
39860         var e = {};
39861         this.fireEvent("beforeremove", this, panel, e);
39862         if(e.cancel === true){
39863             return null;
39864         }
39865         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39866         var panelId = panel.getId();
39867         this.panels.removeKey(panelId);
39868         if(preservePanel){
39869             document.body.appendChild(panel.getEl().dom);
39870         }
39871         if(this.tabs){
39872             this.tabs.removeTab(panel.getEl().id);
39873         }else if (!preservePanel){
39874             this.bodyEl.dom.removeChild(panel.getEl().dom);
39875         }
39876         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39877             var p = this.panels.first();
39878             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39879             tempEl.appendChild(p.getEl().dom);
39880             this.bodyEl.update("");
39881             this.bodyEl.dom.appendChild(p.getEl().dom);
39882             tempEl = null;
39883             this.updateTitle(p.getTitle());
39884             this.tabs = null;
39885             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39886             this.setActivePanel(p);
39887         }
39888         panel.setRegion(null);
39889         if(this.activePanel == panel){
39890             this.activePanel = null;
39891         }
39892         if(this.config.autoDestroy !== false && preservePanel !== true){
39893             try{panel.destroy();}catch(e){}
39894         }
39895         this.fireEvent("panelremoved", this, panel);
39896         return panel;
39897     },
39898
39899     /**
39900      * Returns the TabPanel component used by this region
39901      * @return {Roo.TabPanel}
39902      */
39903     getTabs : function(){
39904         return this.tabs;
39905     },
39906
39907     createTool : function(parentEl, className){
39908         var btn = Roo.DomHelper.append(parentEl, {
39909             tag: "div",
39910             cls: "x-layout-tools-button",
39911             children: [ {
39912                 tag: "div",
39913                 cls: "roo-layout-tools-button-inner " + className,
39914                 html: "&#160;"
39915             }]
39916         }, true);
39917         btn.addClassOnOver("roo-layout-tools-button-over");
39918         return btn;
39919     }
39920 });/*
39921  * Based on:
39922  * Ext JS Library 1.1.1
39923  * Copyright(c) 2006-2007, Ext JS, LLC.
39924  *
39925  * Originally Released Under LGPL - original licence link has changed is not relivant.
39926  *
39927  * Fork - LGPL
39928  * <script type="text/javascript">
39929  */
39930  
39931
39932
39933 /**
39934  * @class Roo.SplitLayoutRegion
39935  * @extends Roo.LayoutRegion
39936  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39937  */
39938 Roo.bootstrap.layout.Split = function(config){
39939     this.cursor = config.cursor;
39940     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39941 };
39942
39943 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39944 {
39945     splitTip : "Drag to resize.",
39946     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39947     useSplitTips : false,
39948
39949     applyConfig : function(config){
39950         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39951     },
39952     
39953     onRender : function(ctr,pos) {
39954         
39955         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39956         if(!this.config.split){
39957             return;
39958         }
39959         if(!this.split){
39960             
39961             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39962                             tag: "div",
39963                             id: this.el.id + "-split",
39964                             cls: "roo-layout-split roo-layout-split-"+this.position,
39965                             html: "&#160;"
39966             });
39967             /** The SplitBar for this region 
39968             * @type Roo.SplitBar */
39969             // does not exist yet...
39970             Roo.log([this.position, this.orientation]);
39971             
39972             this.split = new Roo.bootstrap.SplitBar({
39973                 dragElement : splitEl,
39974                 resizingElement: this.el,
39975                 orientation : this.orientation
39976             });
39977             
39978             this.split.on("moved", this.onSplitMove, this);
39979             this.split.useShim = this.config.useShim === true;
39980             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39981             if(this.useSplitTips){
39982                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39983             }
39984             //if(config.collapsible){
39985             //    this.split.el.on("dblclick", this.collapse,  this);
39986             //}
39987         }
39988         if(typeof this.config.minSize != "undefined"){
39989             this.split.minSize = this.config.minSize;
39990         }
39991         if(typeof this.config.maxSize != "undefined"){
39992             this.split.maxSize = this.config.maxSize;
39993         }
39994         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39995             this.hideSplitter();
39996         }
39997         
39998     },
39999
40000     getHMaxSize : function(){
40001          var cmax = this.config.maxSize || 10000;
40002          var center = this.mgr.getRegion("center");
40003          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40004     },
40005
40006     getVMaxSize : function(){
40007          var cmax = this.config.maxSize || 10000;
40008          var center = this.mgr.getRegion("center");
40009          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40010     },
40011
40012     onSplitMove : function(split, newSize){
40013         this.fireEvent("resized", this, newSize);
40014     },
40015     
40016     /** 
40017      * Returns the {@link Roo.SplitBar} for this region.
40018      * @return {Roo.SplitBar}
40019      */
40020     getSplitBar : function(){
40021         return this.split;
40022     },
40023     
40024     hide : function(){
40025         this.hideSplitter();
40026         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40027     },
40028
40029     hideSplitter : function(){
40030         if(this.split){
40031             this.split.el.setLocation(-2000,-2000);
40032             this.split.el.hide();
40033         }
40034     },
40035
40036     show : function(){
40037         if(this.split){
40038             this.split.el.show();
40039         }
40040         Roo.bootstrap.layout.Split.superclass.show.call(this);
40041     },
40042     
40043     beforeSlide: function(){
40044         if(Roo.isGecko){// firefox overflow auto bug workaround
40045             this.bodyEl.clip();
40046             if(this.tabs) {
40047                 this.tabs.bodyEl.clip();
40048             }
40049             if(this.activePanel){
40050                 this.activePanel.getEl().clip();
40051                 
40052                 if(this.activePanel.beforeSlide){
40053                     this.activePanel.beforeSlide();
40054                 }
40055             }
40056         }
40057     },
40058     
40059     afterSlide : function(){
40060         if(Roo.isGecko){// firefox overflow auto bug workaround
40061             this.bodyEl.unclip();
40062             if(this.tabs) {
40063                 this.tabs.bodyEl.unclip();
40064             }
40065             if(this.activePanel){
40066                 this.activePanel.getEl().unclip();
40067                 if(this.activePanel.afterSlide){
40068                     this.activePanel.afterSlide();
40069                 }
40070             }
40071         }
40072     },
40073
40074     initAutoHide : function(){
40075         if(this.autoHide !== false){
40076             if(!this.autoHideHd){
40077                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40078                 this.autoHideHd = {
40079                     "mouseout": function(e){
40080                         if(!e.within(this.el, true)){
40081                             st.delay(500);
40082                         }
40083                     },
40084                     "mouseover" : function(e){
40085                         st.cancel();
40086                     },
40087                     scope : this
40088                 };
40089             }
40090             this.el.on(this.autoHideHd);
40091         }
40092     },
40093
40094     clearAutoHide : function(){
40095         if(this.autoHide !== false){
40096             this.el.un("mouseout", this.autoHideHd.mouseout);
40097             this.el.un("mouseover", this.autoHideHd.mouseover);
40098         }
40099     },
40100
40101     clearMonitor : function(){
40102         Roo.get(document).un("click", this.slideInIf, this);
40103     },
40104
40105     // these names are backwards but not changed for compat
40106     slideOut : function(){
40107         if(this.isSlid || this.el.hasActiveFx()){
40108             return;
40109         }
40110         this.isSlid = true;
40111         if(this.collapseBtn){
40112             this.collapseBtn.hide();
40113         }
40114         this.closeBtnState = this.closeBtn.getStyle('display');
40115         this.closeBtn.hide();
40116         if(this.stickBtn){
40117             this.stickBtn.show();
40118         }
40119         this.el.show();
40120         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40121         this.beforeSlide();
40122         this.el.setStyle("z-index", 10001);
40123         this.el.slideIn(this.getSlideAnchor(), {
40124             callback: function(){
40125                 this.afterSlide();
40126                 this.initAutoHide();
40127                 Roo.get(document).on("click", this.slideInIf, this);
40128                 this.fireEvent("slideshow", this);
40129             },
40130             scope: this,
40131             block: true
40132         });
40133     },
40134
40135     afterSlideIn : function(){
40136         this.clearAutoHide();
40137         this.isSlid = false;
40138         this.clearMonitor();
40139         this.el.setStyle("z-index", "");
40140         if(this.collapseBtn){
40141             this.collapseBtn.show();
40142         }
40143         this.closeBtn.setStyle('display', this.closeBtnState);
40144         if(this.stickBtn){
40145             this.stickBtn.hide();
40146         }
40147         this.fireEvent("slidehide", this);
40148     },
40149
40150     slideIn : function(cb){
40151         if(!this.isSlid || this.el.hasActiveFx()){
40152             Roo.callback(cb);
40153             return;
40154         }
40155         this.isSlid = false;
40156         this.beforeSlide();
40157         this.el.slideOut(this.getSlideAnchor(), {
40158             callback: function(){
40159                 this.el.setLeftTop(-10000, -10000);
40160                 this.afterSlide();
40161                 this.afterSlideIn();
40162                 Roo.callback(cb);
40163             },
40164             scope: this,
40165             block: true
40166         });
40167     },
40168     
40169     slideInIf : function(e){
40170         if(!e.within(this.el)){
40171             this.slideIn();
40172         }
40173     },
40174
40175     animateCollapse : function(){
40176         this.beforeSlide();
40177         this.el.setStyle("z-index", 20000);
40178         var anchor = this.getSlideAnchor();
40179         this.el.slideOut(anchor, {
40180             callback : function(){
40181                 this.el.setStyle("z-index", "");
40182                 this.collapsedEl.slideIn(anchor, {duration:.3});
40183                 this.afterSlide();
40184                 this.el.setLocation(-10000,-10000);
40185                 this.el.hide();
40186                 this.fireEvent("collapsed", this);
40187             },
40188             scope: this,
40189             block: true
40190         });
40191     },
40192
40193     animateExpand : function(){
40194         this.beforeSlide();
40195         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40196         this.el.setStyle("z-index", 20000);
40197         this.collapsedEl.hide({
40198             duration:.1
40199         });
40200         this.el.slideIn(this.getSlideAnchor(), {
40201             callback : function(){
40202                 this.el.setStyle("z-index", "");
40203                 this.afterSlide();
40204                 if(this.split){
40205                     this.split.el.show();
40206                 }
40207                 this.fireEvent("invalidated", this);
40208                 this.fireEvent("expanded", this);
40209             },
40210             scope: this,
40211             block: true
40212         });
40213     },
40214
40215     anchors : {
40216         "west" : "left",
40217         "east" : "right",
40218         "north" : "top",
40219         "south" : "bottom"
40220     },
40221
40222     sanchors : {
40223         "west" : "l",
40224         "east" : "r",
40225         "north" : "t",
40226         "south" : "b"
40227     },
40228
40229     canchors : {
40230         "west" : "tl-tr",
40231         "east" : "tr-tl",
40232         "north" : "tl-bl",
40233         "south" : "bl-tl"
40234     },
40235
40236     getAnchor : function(){
40237         return this.anchors[this.position];
40238     },
40239
40240     getCollapseAnchor : function(){
40241         return this.canchors[this.position];
40242     },
40243
40244     getSlideAnchor : function(){
40245         return this.sanchors[this.position];
40246     },
40247
40248     getAlignAdj : function(){
40249         var cm = this.cmargins;
40250         switch(this.position){
40251             case "west":
40252                 return [0, 0];
40253             break;
40254             case "east":
40255                 return [0, 0];
40256             break;
40257             case "north":
40258                 return [0, 0];
40259             break;
40260             case "south":
40261                 return [0, 0];
40262             break;
40263         }
40264     },
40265
40266     getExpandAdj : function(){
40267         var c = this.collapsedEl, cm = this.cmargins;
40268         switch(this.position){
40269             case "west":
40270                 return [-(cm.right+c.getWidth()+cm.left), 0];
40271             break;
40272             case "east":
40273                 return [cm.right+c.getWidth()+cm.left, 0];
40274             break;
40275             case "north":
40276                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40277             break;
40278             case "south":
40279                 return [0, cm.top+cm.bottom+c.getHeight()];
40280             break;
40281         }
40282     }
40283 });/*
40284  * Based on:
40285  * Ext JS Library 1.1.1
40286  * Copyright(c) 2006-2007, Ext JS, LLC.
40287  *
40288  * Originally Released Under LGPL - original licence link has changed is not relivant.
40289  *
40290  * Fork - LGPL
40291  * <script type="text/javascript">
40292  */
40293 /*
40294  * These classes are private internal classes
40295  */
40296 Roo.bootstrap.layout.Center = function(config){
40297     config.region = "center";
40298     Roo.bootstrap.layout.Region.call(this, config);
40299     this.visible = true;
40300     this.minWidth = config.minWidth || 20;
40301     this.minHeight = config.minHeight || 20;
40302 };
40303
40304 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40305     hide : function(){
40306         // center panel can't be hidden
40307     },
40308     
40309     show : function(){
40310         // center panel can't be hidden
40311     },
40312     
40313     getMinWidth: function(){
40314         return this.minWidth;
40315     },
40316     
40317     getMinHeight: function(){
40318         return this.minHeight;
40319     }
40320 });
40321
40322
40323
40324
40325  
40326
40327
40328
40329
40330
40331
40332 Roo.bootstrap.layout.North = function(config)
40333 {
40334     config.region = 'north';
40335     config.cursor = 'n-resize';
40336     
40337     Roo.bootstrap.layout.Split.call(this, config);
40338     
40339     
40340     if(this.split){
40341         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40342         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40343         this.split.el.addClass("roo-layout-split-v");
40344     }
40345     //var size = config.initialSize || config.height;
40346     //if(this.el && typeof size != "undefined"){
40347     //    this.el.setHeight(size);
40348     //}
40349 };
40350 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40351 {
40352     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40353      
40354      
40355     onRender : function(ctr, pos)
40356     {
40357         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40358         var size = this.config.initialSize || this.config.height;
40359         if(this.el && typeof size != "undefined"){
40360             this.el.setHeight(size);
40361         }
40362     
40363     },
40364     
40365     getBox : function(){
40366         if(this.collapsed){
40367             return this.collapsedEl.getBox();
40368         }
40369         var box = this.el.getBox();
40370         if(this.split){
40371             box.height += this.split.el.getHeight();
40372         }
40373         return box;
40374     },
40375     
40376     updateBox : function(box){
40377         if(this.split && !this.collapsed){
40378             box.height -= this.split.el.getHeight();
40379             this.split.el.setLeft(box.x);
40380             this.split.el.setTop(box.y+box.height);
40381             this.split.el.setWidth(box.width);
40382         }
40383         if(this.collapsed){
40384             this.updateBody(box.width, null);
40385         }
40386         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40387     }
40388 });
40389
40390
40391
40392
40393
40394 Roo.bootstrap.layout.South = function(config){
40395     config.region = 'south';
40396     config.cursor = 's-resize';
40397     Roo.bootstrap.layout.Split.call(this, config);
40398     if(this.split){
40399         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40400         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40401         this.split.el.addClass("roo-layout-split-v");
40402     }
40403     
40404 };
40405
40406 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40407     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40408     
40409     onRender : function(ctr, pos)
40410     {
40411         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40412         var size = this.config.initialSize || this.config.height;
40413         if(this.el && typeof size != "undefined"){
40414             this.el.setHeight(size);
40415         }
40416     
40417     },
40418     
40419     getBox : function(){
40420         if(this.collapsed){
40421             return this.collapsedEl.getBox();
40422         }
40423         var box = this.el.getBox();
40424         if(this.split){
40425             var sh = this.split.el.getHeight();
40426             box.height += sh;
40427             box.y -= sh;
40428         }
40429         return box;
40430     },
40431     
40432     updateBox : function(box){
40433         if(this.split && !this.collapsed){
40434             var sh = this.split.el.getHeight();
40435             box.height -= sh;
40436             box.y += sh;
40437             this.split.el.setLeft(box.x);
40438             this.split.el.setTop(box.y-sh);
40439             this.split.el.setWidth(box.width);
40440         }
40441         if(this.collapsed){
40442             this.updateBody(box.width, null);
40443         }
40444         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40445     }
40446 });
40447
40448 Roo.bootstrap.layout.East = function(config){
40449     config.region = "east";
40450     config.cursor = "e-resize";
40451     Roo.bootstrap.layout.Split.call(this, config);
40452     if(this.split){
40453         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40454         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40455         this.split.el.addClass("roo-layout-split-h");
40456     }
40457     
40458 };
40459 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40460     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40461     
40462     onRender : function(ctr, pos)
40463     {
40464         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40465         var size = this.config.initialSize || this.config.width;
40466         if(this.el && typeof size != "undefined"){
40467             this.el.setWidth(size);
40468         }
40469     
40470     },
40471     
40472     getBox : function(){
40473         if(this.collapsed){
40474             return this.collapsedEl.getBox();
40475         }
40476         var box = this.el.getBox();
40477         if(this.split){
40478             var sw = this.split.el.getWidth();
40479             box.width += sw;
40480             box.x -= sw;
40481         }
40482         return box;
40483     },
40484
40485     updateBox : function(box){
40486         if(this.split && !this.collapsed){
40487             var sw = this.split.el.getWidth();
40488             box.width -= sw;
40489             this.split.el.setLeft(box.x);
40490             this.split.el.setTop(box.y);
40491             this.split.el.setHeight(box.height);
40492             box.x += sw;
40493         }
40494         if(this.collapsed){
40495             this.updateBody(null, box.height);
40496         }
40497         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40498     }
40499 });
40500
40501 Roo.bootstrap.layout.West = function(config){
40502     config.region = "west";
40503     config.cursor = "w-resize";
40504     
40505     Roo.bootstrap.layout.Split.call(this, config);
40506     if(this.split){
40507         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40508         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40509         this.split.el.addClass("roo-layout-split-h");
40510     }
40511     
40512 };
40513 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40514     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40515     
40516     onRender: function(ctr, pos)
40517     {
40518         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40519         var size = this.config.initialSize || this.config.width;
40520         if(typeof size != "undefined"){
40521             this.el.setWidth(size);
40522         }
40523     },
40524     
40525     getBox : function(){
40526         if(this.collapsed){
40527             return this.collapsedEl.getBox();
40528         }
40529         var box = this.el.getBox();
40530         if (box.width == 0) {
40531             box.width = this.config.width; // kludge?
40532         }
40533         if(this.split){
40534             box.width += this.split.el.getWidth();
40535         }
40536         return box;
40537     },
40538     
40539     updateBox : function(box){
40540         if(this.split && !this.collapsed){
40541             var sw = this.split.el.getWidth();
40542             box.width -= sw;
40543             this.split.el.setLeft(box.x+box.width);
40544             this.split.el.setTop(box.y);
40545             this.split.el.setHeight(box.height);
40546         }
40547         if(this.collapsed){
40548             this.updateBody(null, box.height);
40549         }
40550         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40551     }
40552 });Roo.namespace("Roo.bootstrap.panel");/*
40553  * Based on:
40554  * Ext JS Library 1.1.1
40555  * Copyright(c) 2006-2007, Ext JS, LLC.
40556  *
40557  * Originally Released Under LGPL - original licence link has changed is not relivant.
40558  *
40559  * Fork - LGPL
40560  * <script type="text/javascript">
40561  */
40562 /**
40563  * @class Roo.ContentPanel
40564  * @extends Roo.util.Observable
40565  * @builder-top
40566  * A basic ContentPanel element.
40567  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40568  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40569  * @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
40570  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40571  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40572  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40573  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40574  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40575  * @cfg {String} title          The title for this panel
40576  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40577  * @cfg {String} url            Calls {@link #setUrl} with this value
40578  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40579  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40580  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40581  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40582  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40583  * @cfg {Boolean} badges render the badges
40584  * @cfg {String} cls  extra classes to use  
40585  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40586
40587  * @constructor
40588  * Create a new ContentPanel.
40589  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40590  * @param {String/Object} config A string to set only the title or a config object
40591  * @param {String} content (optional) Set the HTML content for this panel
40592  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40593  */
40594 Roo.bootstrap.panel.Content = function( config){
40595     
40596     this.tpl = config.tpl || false;
40597     
40598     var el = config.el;
40599     var content = config.content;
40600
40601     if(config.autoCreate){ // xtype is available if this is called from factory
40602         el = Roo.id();
40603     }
40604     this.el = Roo.get(el);
40605     if(!this.el && config && config.autoCreate){
40606         if(typeof config.autoCreate == "object"){
40607             if(!config.autoCreate.id){
40608                 config.autoCreate.id = config.id||el;
40609             }
40610             this.el = Roo.DomHelper.append(document.body,
40611                         config.autoCreate, true);
40612         }else{
40613             var elcfg =  {
40614                 tag: "div",
40615                 cls: (config.cls || '') +
40616                     (config.background ? ' bg-' + config.background : '') +
40617                     " roo-layout-inactive-content",
40618                 id: config.id||el
40619             };
40620             if (config.iframe) {
40621                 elcfg.cn = [
40622                     {
40623                         tag : 'iframe',
40624                         style : 'border: 0px',
40625                         src : 'about:blank'
40626                     }
40627                 ];
40628             }
40629               
40630             if (config.html) {
40631                 elcfg.html = config.html;
40632                 
40633             }
40634                         
40635             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40636             if (config.iframe) {
40637                 this.iframeEl = this.el.select('iframe',true).first();
40638             }
40639             
40640         }
40641     } 
40642     this.closable = false;
40643     this.loaded = false;
40644     this.active = false;
40645    
40646       
40647     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40648         
40649         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40650         
40651         this.wrapEl = this.el; //this.el.wrap();
40652         var ti = [];
40653         if (config.toolbar.items) {
40654             ti = config.toolbar.items ;
40655             delete config.toolbar.items ;
40656         }
40657         
40658         var nitems = [];
40659         this.toolbar.render(this.wrapEl, 'before');
40660         for(var i =0;i < ti.length;i++) {
40661           //  Roo.log(['add child', items[i]]);
40662             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40663         }
40664         this.toolbar.items = nitems;
40665         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40666         delete config.toolbar;
40667         
40668     }
40669     /*
40670     // xtype created footer. - not sure if will work as we normally have to render first..
40671     if (this.footer && !this.footer.el && this.footer.xtype) {
40672         if (!this.wrapEl) {
40673             this.wrapEl = this.el.wrap();
40674         }
40675     
40676         this.footer.container = this.wrapEl.createChild();
40677          
40678         this.footer = Roo.factory(this.footer, Roo);
40679         
40680     }
40681     */
40682     
40683      if(typeof config == "string"){
40684         this.title = config;
40685     }else{
40686         Roo.apply(this, config);
40687     }
40688     
40689     if(this.resizeEl){
40690         this.resizeEl = Roo.get(this.resizeEl, true);
40691     }else{
40692         this.resizeEl = this.el;
40693     }
40694     // handle view.xtype
40695     
40696  
40697     
40698     
40699     this.addEvents({
40700         /**
40701          * @event activate
40702          * Fires when this panel is activated. 
40703          * @param {Roo.ContentPanel} this
40704          */
40705         "activate" : true,
40706         /**
40707          * @event deactivate
40708          * Fires when this panel is activated. 
40709          * @param {Roo.ContentPanel} this
40710          */
40711         "deactivate" : true,
40712
40713         /**
40714          * @event resize
40715          * Fires when this panel is resized if fitToFrame is true.
40716          * @param {Roo.ContentPanel} this
40717          * @param {Number} width The width after any component adjustments
40718          * @param {Number} height The height after any component adjustments
40719          */
40720         "resize" : true,
40721         
40722          /**
40723          * @event render
40724          * Fires when this tab is created
40725          * @param {Roo.ContentPanel} this
40726          */
40727         "render" : true,
40728         
40729           /**
40730          * @event scroll
40731          * Fires when this content is scrolled
40732          * @param {Roo.ContentPanel} this
40733          * @param {Event} scrollEvent
40734          */
40735         "scroll" : true
40736         
40737         
40738         
40739     });
40740     
40741
40742     
40743     
40744     if(this.autoScroll && !this.iframe){
40745         this.resizeEl.setStyle("overflow", "auto");
40746         this.resizeEl.on('scroll', this.onScroll, this);
40747     } else {
40748         // fix randome scrolling
40749         //this.el.on('scroll', function() {
40750         //    Roo.log('fix random scolling');
40751         //    this.scrollTo('top',0); 
40752         //});
40753     }
40754     content = content || this.content;
40755     if(content){
40756         this.setContent(content);
40757     }
40758     if(config && config.url){
40759         this.setUrl(this.url, this.params, this.loadOnce);
40760     }
40761     
40762     
40763     
40764     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40765     
40766     if (this.view && typeof(this.view.xtype) != 'undefined') {
40767         this.view.el = this.el.appendChild(document.createElement("div"));
40768         this.view = Roo.factory(this.view); 
40769         this.view.render  &&  this.view.render(false, '');  
40770     }
40771     
40772     
40773     this.fireEvent('render', this);
40774 };
40775
40776 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40777     
40778     cls : '',
40779     background : '',
40780     
40781     tabTip : '',
40782     
40783     iframe : false,
40784     iframeEl : false,
40785     
40786     /* Resize Element - use this to work out scroll etc. */
40787     resizeEl : false,
40788     
40789     setRegion : function(region){
40790         this.region = region;
40791         this.setActiveClass(region && !this.background);
40792     },
40793     
40794     
40795     setActiveClass: function(state)
40796     {
40797         if(state){
40798            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40799            this.el.setStyle('position','relative');
40800         }else{
40801            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40802            this.el.setStyle('position', 'absolute');
40803         } 
40804     },
40805     
40806     /**
40807      * Returns the toolbar for this Panel if one was configured. 
40808      * @return {Roo.Toolbar} 
40809      */
40810     getToolbar : function(){
40811         return this.toolbar;
40812     },
40813     
40814     setActiveState : function(active)
40815     {
40816         this.active = active;
40817         this.setActiveClass(active);
40818         if(!active){
40819             if(this.fireEvent("deactivate", this) === false){
40820                 return false;
40821             }
40822             return true;
40823         }
40824         this.fireEvent("activate", this);
40825         return true;
40826     },
40827     /**
40828      * Updates this panel's element (not for iframe)
40829      * @param {String} content The new content
40830      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40831     */
40832     setContent : function(content, loadScripts){
40833         if (this.iframe) {
40834             return;
40835         }
40836         
40837         this.el.update(content, loadScripts);
40838     },
40839
40840     ignoreResize : function(w, h){
40841         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40842             return true;
40843         }else{
40844             this.lastSize = {width: w, height: h};
40845             return false;
40846         }
40847     },
40848     /**
40849      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40850      * @return {Roo.UpdateManager} The UpdateManager
40851      */
40852     getUpdateManager : function(){
40853         if (this.iframe) {
40854             return false;
40855         }
40856         return this.el.getUpdateManager();
40857     },
40858      /**
40859      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40860      * Does not work with IFRAME contents
40861      * @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:
40862 <pre><code>
40863 panel.load({
40864     url: "your-url.php",
40865     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40866     callback: yourFunction,
40867     scope: yourObject, //(optional scope)
40868     discardUrl: false,
40869     nocache: false,
40870     text: "Loading...",
40871     timeout: 30,
40872     scripts: false
40873 });
40874 </code></pre>
40875      
40876      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40877      * 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.
40878      * @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}
40879      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40880      * @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.
40881      * @return {Roo.ContentPanel} this
40882      */
40883     load : function(){
40884         
40885         if (this.iframe) {
40886             return this;
40887         }
40888         
40889         var um = this.el.getUpdateManager();
40890         um.update.apply(um, arguments);
40891         return this;
40892     },
40893
40894
40895     /**
40896      * 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.
40897      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40898      * @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)
40899      * @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)
40900      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40901      */
40902     setUrl : function(url, params, loadOnce){
40903         if (this.iframe) {
40904             this.iframeEl.dom.src = url;
40905             return false;
40906         }
40907         
40908         if(this.refreshDelegate){
40909             this.removeListener("activate", this.refreshDelegate);
40910         }
40911         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40912         this.on("activate", this.refreshDelegate);
40913         return this.el.getUpdateManager();
40914     },
40915     
40916     _handleRefresh : function(url, params, loadOnce){
40917         if(!loadOnce || !this.loaded){
40918             var updater = this.el.getUpdateManager();
40919             updater.update(url, params, this._setLoaded.createDelegate(this));
40920         }
40921     },
40922     
40923     _setLoaded : function(){
40924         this.loaded = true;
40925     }, 
40926     
40927     /**
40928      * Returns this panel's id
40929      * @return {String} 
40930      */
40931     getId : function(){
40932         return this.el.id;
40933     },
40934     
40935     /** 
40936      * Returns this panel's element - used by regiosn to add.
40937      * @return {Roo.Element} 
40938      */
40939     getEl : function(){
40940         return this.wrapEl || this.el;
40941     },
40942     
40943    
40944     
40945     adjustForComponents : function(width, height)
40946     {
40947         //Roo.log('adjustForComponents ');
40948         if(this.resizeEl != this.el){
40949             width -= this.el.getFrameWidth('lr');
40950             height -= this.el.getFrameWidth('tb');
40951         }
40952         if(this.toolbar){
40953             var te = this.toolbar.getEl();
40954             te.setWidth(width);
40955             height -= te.getHeight();
40956         }
40957         if(this.footer){
40958             var te = this.footer.getEl();
40959             te.setWidth(width);
40960             height -= te.getHeight();
40961         }
40962         
40963         
40964         if(this.adjustments){
40965             width += this.adjustments[0];
40966             height += this.adjustments[1];
40967         }
40968         return {"width": width, "height": height};
40969     },
40970     
40971     setSize : function(width, height){
40972         if(this.fitToFrame && !this.ignoreResize(width, height)){
40973             if(this.fitContainer && this.resizeEl != this.el){
40974                 this.el.setSize(width, height);
40975             }
40976             var size = this.adjustForComponents(width, height);
40977             if (this.iframe) {
40978                 this.iframeEl.setSize(width,height);
40979             }
40980             
40981             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40982             this.fireEvent('resize', this, size.width, size.height);
40983             
40984             
40985         }
40986     },
40987     
40988     /**
40989      * Returns this panel's title
40990      * @return {String} 
40991      */
40992     getTitle : function(){
40993         
40994         if (typeof(this.title) != 'object') {
40995             return this.title;
40996         }
40997         
40998         var t = '';
40999         for (var k in this.title) {
41000             if (!this.title.hasOwnProperty(k)) {
41001                 continue;
41002             }
41003             
41004             if (k.indexOf('-') >= 0) {
41005                 var s = k.split('-');
41006                 for (var i = 0; i<s.length; i++) {
41007                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41008                 }
41009             } else {
41010                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41011             }
41012         }
41013         return t;
41014     },
41015     
41016     /**
41017      * Set this panel's title
41018      * @param {String} title
41019      */
41020     setTitle : function(title){
41021         this.title = title;
41022         if(this.region){
41023             this.region.updatePanelTitle(this, title);
41024         }
41025     },
41026     
41027     /**
41028      * Returns true is this panel was configured to be closable
41029      * @return {Boolean} 
41030      */
41031     isClosable : function(){
41032         return this.closable;
41033     },
41034     
41035     beforeSlide : function(){
41036         this.el.clip();
41037         this.resizeEl.clip();
41038     },
41039     
41040     afterSlide : function(){
41041         this.el.unclip();
41042         this.resizeEl.unclip();
41043     },
41044     
41045     /**
41046      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41047      *   Will fail silently if the {@link #setUrl} method has not been called.
41048      *   This does not activate the panel, just updates its content.
41049      */
41050     refresh : function(){
41051         if(this.refreshDelegate){
41052            this.loaded = false;
41053            this.refreshDelegate();
41054         }
41055     },
41056     
41057     /**
41058      * Destroys this panel
41059      */
41060     destroy : function(){
41061         this.el.removeAllListeners();
41062         var tempEl = document.createElement("span");
41063         tempEl.appendChild(this.el.dom);
41064         tempEl.innerHTML = "";
41065         this.el.remove();
41066         this.el = null;
41067     },
41068     
41069     /**
41070      * form - if the content panel contains a form - this is a reference to it.
41071      * @type {Roo.form.Form}
41072      */
41073     form : false,
41074     /**
41075      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41076      *    This contains a reference to it.
41077      * @type {Roo.View}
41078      */
41079     view : false,
41080     
41081       /**
41082      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41083      * <pre><code>
41084
41085 layout.addxtype({
41086        xtype : 'Form',
41087        items: [ .... ]
41088    }
41089 );
41090
41091 </code></pre>
41092      * @param {Object} cfg Xtype definition of item to add.
41093      */
41094     
41095     
41096     getChildContainer: function () {
41097         return this.getEl();
41098     },
41099     
41100     
41101     onScroll : function(e)
41102     {
41103         this.fireEvent('scroll', this, e);
41104     }
41105     
41106     
41107     /*
41108         var  ret = new Roo.factory(cfg);
41109         return ret;
41110         
41111         
41112         // add form..
41113         if (cfg.xtype.match(/^Form$/)) {
41114             
41115             var el;
41116             //if (this.footer) {
41117             //    el = this.footer.container.insertSibling(false, 'before');
41118             //} else {
41119                 el = this.el.createChild();
41120             //}
41121
41122             this.form = new  Roo.form.Form(cfg);
41123             
41124             
41125             if ( this.form.allItems.length) {
41126                 this.form.render(el.dom);
41127             }
41128             return this.form;
41129         }
41130         // should only have one of theses..
41131         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41132             // views.. should not be just added - used named prop 'view''
41133             
41134             cfg.el = this.el.appendChild(document.createElement("div"));
41135             // factory?
41136             
41137             var ret = new Roo.factory(cfg);
41138              
41139              ret.render && ret.render(false, ''); // render blank..
41140             this.view = ret;
41141             return ret;
41142         }
41143         return false;
41144     }
41145     \*/
41146 });
41147  
41148 /**
41149  * @class Roo.bootstrap.panel.Grid
41150  * @extends Roo.bootstrap.panel.Content
41151  * @constructor
41152  * Create a new GridPanel.
41153  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41154  * @param {Object} config A the config object
41155   
41156  */
41157
41158
41159
41160 Roo.bootstrap.panel.Grid = function(config)
41161 {
41162     
41163       
41164     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41165         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41166
41167     config.el = this.wrapper;
41168     //this.el = this.wrapper;
41169     
41170       if (config.container) {
41171         // ctor'ed from a Border/panel.grid
41172         
41173         
41174         this.wrapper.setStyle("overflow", "hidden");
41175         this.wrapper.addClass('roo-grid-container');
41176
41177     }
41178     
41179     
41180     if(config.toolbar){
41181         var tool_el = this.wrapper.createChild();    
41182         this.toolbar = Roo.factory(config.toolbar);
41183         var ti = [];
41184         if (config.toolbar.items) {
41185             ti = config.toolbar.items ;
41186             delete config.toolbar.items ;
41187         }
41188         
41189         var nitems = [];
41190         this.toolbar.render(tool_el);
41191         for(var i =0;i < ti.length;i++) {
41192           //  Roo.log(['add child', items[i]]);
41193             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41194         }
41195         this.toolbar.items = nitems;
41196         
41197         delete config.toolbar;
41198     }
41199     
41200     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41201     config.grid.scrollBody = true;;
41202     config.grid.monitorWindowResize = false; // turn off autosizing
41203     config.grid.autoHeight = false;
41204     config.grid.autoWidth = false;
41205     
41206     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41207     
41208     if (config.background) {
41209         // render grid on panel activation (if panel background)
41210         this.on('activate', function(gp) {
41211             if (!gp.grid.rendered) {
41212                 gp.grid.render(this.wrapper);
41213                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41214             }
41215         });
41216             
41217     } else {
41218         this.grid.render(this.wrapper);
41219         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41220
41221     }
41222     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41223     // ??? needed ??? config.el = this.wrapper;
41224     
41225     
41226     
41227   
41228     // xtype created footer. - not sure if will work as we normally have to render first..
41229     if (this.footer && !this.footer.el && this.footer.xtype) {
41230         
41231         var ctr = this.grid.getView().getFooterPanel(true);
41232         this.footer.dataSource = this.grid.dataSource;
41233         this.footer = Roo.factory(this.footer, Roo);
41234         this.footer.render(ctr);
41235         
41236     }
41237     
41238     
41239     
41240     
41241      
41242 };
41243
41244 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41245     getId : function(){
41246         return this.grid.id;
41247     },
41248     
41249     /**
41250      * Returns the grid for this panel
41251      * @return {Roo.bootstrap.Table} 
41252      */
41253     getGrid : function(){
41254         return this.grid;    
41255     },
41256     
41257     setSize : function(width, height){
41258         if(!this.ignoreResize(width, height)){
41259             var grid = this.grid;
41260             var size = this.adjustForComponents(width, height);
41261             // tfoot is not a footer?
41262           
41263             
41264             var gridel = grid.getGridEl();
41265             gridel.setSize(size.width, size.height);
41266             
41267             var tbd = grid.getGridEl().select('tbody', true).first();
41268             var thd = grid.getGridEl().select('thead',true).first();
41269             var tbf= grid.getGridEl().select('tfoot', true).first();
41270
41271             if (tbf) {
41272                 size.height -= tbf.getHeight();
41273             }
41274             if (thd) {
41275                 size.height -= thd.getHeight();
41276             }
41277             
41278             tbd.setSize(size.width, size.height );
41279             // this is for the account management tab -seems to work there.
41280             var thd = grid.getGridEl().select('thead',true).first();
41281             //if (tbd) {
41282             //    tbd.setSize(size.width, size.height - thd.getHeight());
41283             //}
41284              
41285             grid.autoSize();
41286         }
41287     },
41288      
41289     
41290     
41291     beforeSlide : function(){
41292         this.grid.getView().scroller.clip();
41293     },
41294     
41295     afterSlide : function(){
41296         this.grid.getView().scroller.unclip();
41297     },
41298     
41299     destroy : function(){
41300         this.grid.destroy();
41301         delete this.grid;
41302         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41303     }
41304 });
41305
41306 /**
41307  * @class Roo.bootstrap.panel.Nest
41308  * @extends Roo.bootstrap.panel.Content
41309  * @constructor
41310  * Create a new Panel, that can contain a layout.Border.
41311  * 
41312  * 
41313  * @param {Roo.BorderLayout} layout The layout for this panel
41314  * @param {String/Object} config A string to set only the title or a config object
41315  */
41316 Roo.bootstrap.panel.Nest = function(config)
41317 {
41318     // construct with only one argument..
41319     /* FIXME - implement nicer consturctors
41320     if (layout.layout) {
41321         config = layout;
41322         layout = config.layout;
41323         delete config.layout;
41324     }
41325     if (layout.xtype && !layout.getEl) {
41326         // then layout needs constructing..
41327         layout = Roo.factory(layout, Roo);
41328     }
41329     */
41330     
41331     config.el =  config.layout.getEl();
41332     
41333     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41334     
41335     config.layout.monitorWindowResize = false; // turn off autosizing
41336     this.layout = config.layout;
41337     this.layout.getEl().addClass("roo-layout-nested-layout");
41338     this.layout.parent = this;
41339     
41340     
41341     
41342     
41343 };
41344
41345 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41346
41347     setSize : function(width, height){
41348         if(!this.ignoreResize(width, height)){
41349             var size = this.adjustForComponents(width, height);
41350             var el = this.layout.getEl();
41351             if (size.height < 1) {
41352                 el.setWidth(size.width);   
41353             } else {
41354                 el.setSize(size.width, size.height);
41355             }
41356             var touch = el.dom.offsetWidth;
41357             this.layout.layout();
41358             // ie requires a double layout on the first pass
41359             if(Roo.isIE && !this.initialized){
41360                 this.initialized = true;
41361                 this.layout.layout();
41362             }
41363         }
41364     },
41365     
41366     // activate all subpanels if not currently active..
41367     
41368     setActiveState : function(active){
41369         this.active = active;
41370         this.setActiveClass(active);
41371         
41372         if(!active){
41373             this.fireEvent("deactivate", this);
41374             return;
41375         }
41376         
41377         this.fireEvent("activate", this);
41378         // not sure if this should happen before or after..
41379         if (!this.layout) {
41380             return; // should not happen..
41381         }
41382         var reg = false;
41383         for (var r in this.layout.regions) {
41384             reg = this.layout.getRegion(r);
41385             if (reg.getActivePanel()) {
41386                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41387                 reg.setActivePanel(reg.getActivePanel());
41388                 continue;
41389             }
41390             if (!reg.panels.length) {
41391                 continue;
41392             }
41393             reg.showPanel(reg.getPanel(0));
41394         }
41395         
41396         
41397         
41398         
41399     },
41400     
41401     /**
41402      * Returns the nested BorderLayout for this panel
41403      * @return {Roo.BorderLayout} 
41404      */
41405     getLayout : function(){
41406         return this.layout;
41407     },
41408     
41409      /**
41410      * Adds a xtype elements to the layout of the nested panel
41411      * <pre><code>
41412
41413 panel.addxtype({
41414        xtype : 'ContentPanel',
41415        region: 'west',
41416        items: [ .... ]
41417    }
41418 );
41419
41420 panel.addxtype({
41421         xtype : 'NestedLayoutPanel',
41422         region: 'west',
41423         layout: {
41424            center: { },
41425            west: { }   
41426         },
41427         items : [ ... list of content panels or nested layout panels.. ]
41428    }
41429 );
41430 </code></pre>
41431      * @param {Object} cfg Xtype definition of item to add.
41432      */
41433     addxtype : function(cfg) {
41434         return this.layout.addxtype(cfg);
41435     
41436     }
41437 });/*
41438  * Based on:
41439  * Ext JS Library 1.1.1
41440  * Copyright(c) 2006-2007, Ext JS, LLC.
41441  *
41442  * Originally Released Under LGPL - original licence link has changed is not relivant.
41443  *
41444  * Fork - LGPL
41445  * <script type="text/javascript">
41446  */
41447 /**
41448  * @class Roo.TabPanel
41449  * @extends Roo.util.Observable
41450  * A lightweight tab container.
41451  * <br><br>
41452  * Usage:
41453  * <pre><code>
41454 // basic tabs 1, built from existing content
41455 var tabs = new Roo.TabPanel("tabs1");
41456 tabs.addTab("script", "View Script");
41457 tabs.addTab("markup", "View Markup");
41458 tabs.activate("script");
41459
41460 // more advanced tabs, built from javascript
41461 var jtabs = new Roo.TabPanel("jtabs");
41462 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41463
41464 // set up the UpdateManager
41465 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41466 var updater = tab2.getUpdateManager();
41467 updater.setDefaultUrl("ajax1.htm");
41468 tab2.on('activate', updater.refresh, updater, true);
41469
41470 // Use setUrl for Ajax loading
41471 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41472 tab3.setUrl("ajax2.htm", null, true);
41473
41474 // Disabled tab
41475 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41476 tab4.disable();
41477
41478 jtabs.activate("jtabs-1");
41479  * </code></pre>
41480  * @constructor
41481  * Create a new TabPanel.
41482  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41483  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41484  */
41485 Roo.bootstrap.panel.Tabs = function(config){
41486     /**
41487     * The container element for this TabPanel.
41488     * @type Roo.Element
41489     */
41490     this.el = Roo.get(config.el);
41491     delete config.el;
41492     if(config){
41493         if(typeof config == "boolean"){
41494             this.tabPosition = config ? "bottom" : "top";
41495         }else{
41496             Roo.apply(this, config);
41497         }
41498     }
41499     
41500     if(this.tabPosition == "bottom"){
41501         // if tabs are at the bottom = create the body first.
41502         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41503         this.el.addClass("roo-tabs-bottom");
41504     }
41505     // next create the tabs holders
41506     
41507     if (this.tabPosition == "west"){
41508         
41509         var reg = this.region; // fake it..
41510         while (reg) {
41511             if (!reg.mgr.parent) {
41512                 break;
41513             }
41514             reg = reg.mgr.parent.region;
41515         }
41516         Roo.log("got nest?");
41517         Roo.log(reg);
41518         if (reg.mgr.getRegion('west')) {
41519             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41520             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41521             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41522             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41523             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41524         
41525             
41526         }
41527         
41528         
41529     } else {
41530      
41531         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41532         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41533         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41534         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41535     }
41536     
41537     
41538     if(Roo.isIE){
41539         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41540     }
41541     
41542     // finally - if tabs are at the top, then create the body last..
41543     if(this.tabPosition != "bottom"){
41544         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41545          * @type Roo.Element
41546          */
41547         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41548         this.el.addClass("roo-tabs-top");
41549     }
41550     this.items = [];
41551
41552     this.bodyEl.setStyle("position", "relative");
41553
41554     this.active = null;
41555     this.activateDelegate = this.activate.createDelegate(this);
41556
41557     this.addEvents({
41558         /**
41559          * @event tabchange
41560          * Fires when the active tab changes
41561          * @param {Roo.TabPanel} this
41562          * @param {Roo.TabPanelItem} activePanel The new active tab
41563          */
41564         "tabchange": true,
41565         /**
41566          * @event beforetabchange
41567          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41568          * @param {Roo.TabPanel} this
41569          * @param {Object} e Set cancel to true on this object to cancel the tab change
41570          * @param {Roo.TabPanelItem} tab The tab being changed to
41571          */
41572         "beforetabchange" : true
41573     });
41574
41575     Roo.EventManager.onWindowResize(this.onResize, this);
41576     this.cpad = this.el.getPadding("lr");
41577     this.hiddenCount = 0;
41578
41579
41580     // toolbar on the tabbar support...
41581     if (this.toolbar) {
41582         alert("no toolbar support yet");
41583         this.toolbar  = false;
41584         /*
41585         var tcfg = this.toolbar;
41586         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41587         this.toolbar = new Roo.Toolbar(tcfg);
41588         if (Roo.isSafari) {
41589             var tbl = tcfg.container.child('table', true);
41590             tbl.setAttribute('width', '100%');
41591         }
41592         */
41593         
41594     }
41595    
41596
41597
41598     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41599 };
41600
41601 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41602     /*
41603      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41604      */
41605     tabPosition : "top",
41606     /*
41607      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41608      */
41609     currentTabWidth : 0,
41610     /*
41611      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41612      */
41613     minTabWidth : 40,
41614     /*
41615      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41616      */
41617     maxTabWidth : 250,
41618     /*
41619      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41620      */
41621     preferredTabWidth : 175,
41622     /*
41623      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41624      */
41625     resizeTabs : false,
41626     /*
41627      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41628      */
41629     monitorResize : true,
41630     /*
41631      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41632      */
41633     toolbar : false,  // set by caller..
41634     
41635     region : false, /// set by caller
41636     
41637     disableTooltips : true, // not used yet...
41638
41639     /**
41640      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41641      * @param {String} id The id of the div to use <b>or create</b>
41642      * @param {String} text The text for the tab
41643      * @param {String} content (optional) Content to put in the TabPanelItem body
41644      * @param {Boolean} closable (optional) True to create a close icon on the tab
41645      * @return {Roo.TabPanelItem} The created TabPanelItem
41646      */
41647     addTab : function(id, text, content, closable, tpl)
41648     {
41649         var item = new Roo.bootstrap.panel.TabItem({
41650             panel: this,
41651             id : id,
41652             text : text,
41653             closable : closable,
41654             tpl : tpl
41655         });
41656         this.addTabItem(item);
41657         if(content){
41658             item.setContent(content);
41659         }
41660         return item;
41661     },
41662
41663     /**
41664      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41665      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41666      * @return {Roo.TabPanelItem}
41667      */
41668     getTab : function(id){
41669         return this.items[id];
41670     },
41671
41672     /**
41673      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41674      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41675      */
41676     hideTab : function(id){
41677         var t = this.items[id];
41678         if(!t.isHidden()){
41679            t.setHidden(true);
41680            this.hiddenCount++;
41681            this.autoSizeTabs();
41682         }
41683     },
41684
41685     /**
41686      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41687      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41688      */
41689     unhideTab : function(id){
41690         var t = this.items[id];
41691         if(t.isHidden()){
41692            t.setHidden(false);
41693            this.hiddenCount--;
41694            this.autoSizeTabs();
41695         }
41696     },
41697
41698     /**
41699      * Adds an existing {@link Roo.TabPanelItem}.
41700      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41701      */
41702     addTabItem : function(item)
41703     {
41704         this.items[item.id] = item;
41705         this.items.push(item);
41706         this.autoSizeTabs();
41707       //  if(this.resizeTabs){
41708     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41709   //         this.autoSizeTabs();
41710 //        }else{
41711 //            item.autoSize();
41712        // }
41713     },
41714
41715     /**
41716      * Removes a {@link Roo.TabPanelItem}.
41717      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41718      */
41719     removeTab : function(id){
41720         var items = this.items;
41721         var tab = items[id];
41722         if(!tab) { return; }
41723         var index = items.indexOf(tab);
41724         if(this.active == tab && items.length > 1){
41725             var newTab = this.getNextAvailable(index);
41726             if(newTab) {
41727                 newTab.activate();
41728             }
41729         }
41730         this.stripEl.dom.removeChild(tab.pnode.dom);
41731         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41732             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41733         }
41734         items.splice(index, 1);
41735         delete this.items[tab.id];
41736         tab.fireEvent("close", tab);
41737         tab.purgeListeners();
41738         this.autoSizeTabs();
41739     },
41740
41741     getNextAvailable : function(start){
41742         var items = this.items;
41743         var index = start;
41744         // look for a next tab that will slide over to
41745         // replace the one being removed
41746         while(index < items.length){
41747             var item = items[++index];
41748             if(item && !item.isHidden()){
41749                 return item;
41750             }
41751         }
41752         // if one isn't found select the previous tab (on the left)
41753         index = start;
41754         while(index >= 0){
41755             var item = items[--index];
41756             if(item && !item.isHidden()){
41757                 return item;
41758             }
41759         }
41760         return null;
41761     },
41762
41763     /**
41764      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41765      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41766      */
41767     disableTab : function(id){
41768         var tab = this.items[id];
41769         if(tab && this.active != tab){
41770             tab.disable();
41771         }
41772     },
41773
41774     /**
41775      * Enables a {@link Roo.TabPanelItem} that is disabled.
41776      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41777      */
41778     enableTab : function(id){
41779         var tab = this.items[id];
41780         tab.enable();
41781     },
41782
41783     /**
41784      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41785      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41786      * @return {Roo.TabPanelItem} The TabPanelItem.
41787      */
41788     activate : function(id)
41789     {
41790         //Roo.log('activite:'  + id);
41791         
41792         var tab = this.items[id];
41793         if(!tab){
41794             return null;
41795         }
41796         if(tab == this.active || tab.disabled){
41797             return tab;
41798         }
41799         var e = {};
41800         this.fireEvent("beforetabchange", this, e, tab);
41801         if(e.cancel !== true && !tab.disabled){
41802             if(this.active){
41803                 this.active.hide();
41804             }
41805             this.active = this.items[id];
41806             this.active.show();
41807             this.fireEvent("tabchange", this, this.active);
41808         }
41809         return tab;
41810     },
41811
41812     /**
41813      * Gets the active {@link Roo.TabPanelItem}.
41814      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41815      */
41816     getActiveTab : function(){
41817         return this.active;
41818     },
41819
41820     /**
41821      * Updates the tab body element to fit the height of the container element
41822      * for overflow scrolling
41823      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41824      */
41825     syncHeight : function(targetHeight){
41826         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41827         var bm = this.bodyEl.getMargins();
41828         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41829         this.bodyEl.setHeight(newHeight);
41830         return newHeight;
41831     },
41832
41833     onResize : function(){
41834         if(this.monitorResize){
41835             this.autoSizeTabs();
41836         }
41837     },
41838
41839     /**
41840      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41841      */
41842     beginUpdate : function(){
41843         this.updating = true;
41844     },
41845
41846     /**
41847      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41848      */
41849     endUpdate : function(){
41850         this.updating = false;
41851         this.autoSizeTabs();
41852     },
41853
41854     /**
41855      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41856      */
41857     autoSizeTabs : function()
41858     {
41859         var count = this.items.length;
41860         var vcount = count - this.hiddenCount;
41861         
41862         if (vcount < 2) {
41863             this.stripEl.hide();
41864         } else {
41865             this.stripEl.show();
41866         }
41867         
41868         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41869             return;
41870         }
41871         
41872         
41873         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41874         var availWidth = Math.floor(w / vcount);
41875         var b = this.stripBody;
41876         if(b.getWidth() > w){
41877             var tabs = this.items;
41878             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41879             if(availWidth < this.minTabWidth){
41880                 /*if(!this.sleft){    // incomplete scrolling code
41881                     this.createScrollButtons();
41882                 }
41883                 this.showScroll();
41884                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41885             }
41886         }else{
41887             if(this.currentTabWidth < this.preferredTabWidth){
41888                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41889             }
41890         }
41891     },
41892
41893     /**
41894      * Returns the number of tabs in this TabPanel.
41895      * @return {Number}
41896      */
41897      getCount : function(){
41898          return this.items.length;
41899      },
41900
41901     /**
41902      * Resizes all the tabs to the passed width
41903      * @param {Number} The new width
41904      */
41905     setTabWidth : function(width){
41906         this.currentTabWidth = width;
41907         for(var i = 0, len = this.items.length; i < len; i++) {
41908                 if(!this.items[i].isHidden()) {
41909                 this.items[i].setWidth(width);
41910             }
41911         }
41912     },
41913
41914     /**
41915      * Destroys this TabPanel
41916      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41917      */
41918     destroy : function(removeEl){
41919         Roo.EventManager.removeResizeListener(this.onResize, this);
41920         for(var i = 0, len = this.items.length; i < len; i++){
41921             this.items[i].purgeListeners();
41922         }
41923         if(removeEl === true){
41924             this.el.update("");
41925             this.el.remove();
41926         }
41927     },
41928     
41929     createStrip : function(container)
41930     {
41931         var strip = document.createElement("nav");
41932         strip.className = Roo.bootstrap.version == 4 ?
41933             "navbar-light bg-light" : 
41934             "navbar navbar-default"; //"x-tabs-wrap";
41935         container.appendChild(strip);
41936         return strip;
41937     },
41938     
41939     createStripList : function(strip)
41940     {
41941         // div wrapper for retard IE
41942         // returns the "tr" element.
41943         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41944         //'<div class="x-tabs-strip-wrap">'+
41945           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41946           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41947         return strip.firstChild; //.firstChild.firstChild.firstChild;
41948     },
41949     createBody : function(container)
41950     {
41951         var body = document.createElement("div");
41952         Roo.id(body, "tab-body");
41953         //Roo.fly(body).addClass("x-tabs-body");
41954         Roo.fly(body).addClass("tab-content");
41955         container.appendChild(body);
41956         return body;
41957     },
41958     createItemBody :function(bodyEl, id){
41959         var body = Roo.getDom(id);
41960         if(!body){
41961             body = document.createElement("div");
41962             body.id = id;
41963         }
41964         //Roo.fly(body).addClass("x-tabs-item-body");
41965         Roo.fly(body).addClass("tab-pane");
41966          bodyEl.insertBefore(body, bodyEl.firstChild);
41967         return body;
41968     },
41969     /** @private */
41970     createStripElements :  function(stripEl, text, closable, tpl)
41971     {
41972         var td = document.createElement("li"); // was td..
41973         td.className = 'nav-item';
41974         
41975         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41976         
41977         
41978         stripEl.appendChild(td);
41979         /*if(closable){
41980             td.className = "x-tabs-closable";
41981             if(!this.closeTpl){
41982                 this.closeTpl = new Roo.Template(
41983                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41984                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41985                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41986                 );
41987             }
41988             var el = this.closeTpl.overwrite(td, {"text": text});
41989             var close = el.getElementsByTagName("div")[0];
41990             var inner = el.getElementsByTagName("em")[0];
41991             return {"el": el, "close": close, "inner": inner};
41992         } else {
41993         */
41994         // not sure what this is..
41995 //            if(!this.tabTpl){
41996                 //this.tabTpl = new Roo.Template(
41997                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41998                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41999                 //);
42000 //                this.tabTpl = new Roo.Template(
42001 //                   '<a href="#">' +
42002 //                   '<span unselectable="on"' +
42003 //                            (this.disableTooltips ? '' : ' title="{text}"') +
42004 //                            ' >{text}</span></a>'
42005 //                );
42006 //                
42007 //            }
42008
42009
42010             var template = tpl || this.tabTpl || false;
42011             
42012             if(!template){
42013                 template =  new Roo.Template(
42014                         Roo.bootstrap.version == 4 ? 
42015                             (
42016                                 '<a class="nav-link" href="#" unselectable="on"' +
42017                                      (this.disableTooltips ? '' : ' title="{text}"') +
42018                                      ' >{text}</a>'
42019                             ) : (
42020                                 '<a class="nav-link" href="#">' +
42021                                 '<span unselectable="on"' +
42022                                          (this.disableTooltips ? '' : ' title="{text}"') +
42023                                     ' >{text}</span></a>'
42024                             )
42025                 );
42026             }
42027             
42028             switch (typeof(template)) {
42029                 case 'object' :
42030                     break;
42031                 case 'string' :
42032                     template = new Roo.Template(template);
42033                     break;
42034                 default :
42035                     break;
42036             }
42037             
42038             var el = template.overwrite(td, {"text": text});
42039             
42040             var inner = el.getElementsByTagName("span")[0];
42041             
42042             return {"el": el, "inner": inner};
42043             
42044     }
42045         
42046     
42047 });
42048
42049 /**
42050  * @class Roo.TabPanelItem
42051  * @extends Roo.util.Observable
42052  * Represents an individual item (tab plus body) in a TabPanel.
42053  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42054  * @param {String} id The id of this TabPanelItem
42055  * @param {String} text The text for the tab of this TabPanelItem
42056  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42057  */
42058 Roo.bootstrap.panel.TabItem = function(config){
42059     /**
42060      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42061      * @type Roo.TabPanel
42062      */
42063     this.tabPanel = config.panel;
42064     /**
42065      * The id for this TabPanelItem
42066      * @type String
42067      */
42068     this.id = config.id;
42069     /** @private */
42070     this.disabled = false;
42071     /** @private */
42072     this.text = config.text;
42073     /** @private */
42074     this.loaded = false;
42075     this.closable = config.closable;
42076
42077     /**
42078      * The body element for this TabPanelItem.
42079      * @type Roo.Element
42080      */
42081     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42082     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42083     this.bodyEl.setStyle("display", "block");
42084     this.bodyEl.setStyle("zoom", "1");
42085     //this.hideAction();
42086
42087     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42088     /** @private */
42089     this.el = Roo.get(els.el);
42090     this.inner = Roo.get(els.inner, true);
42091      this.textEl = Roo.bootstrap.version == 4 ?
42092         this.el : Roo.get(this.el.dom.firstChild, true);
42093
42094     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42095     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42096
42097     
42098 //    this.el.on("mousedown", this.onTabMouseDown, this);
42099     this.el.on("click", this.onTabClick, this);
42100     /** @private */
42101     if(config.closable){
42102         var c = Roo.get(els.close, true);
42103         c.dom.title = this.closeText;
42104         c.addClassOnOver("close-over");
42105         c.on("click", this.closeClick, this);
42106      }
42107
42108     this.addEvents({
42109          /**
42110          * @event activate
42111          * Fires when this tab becomes the active tab.
42112          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42113          * @param {Roo.TabPanelItem} this
42114          */
42115         "activate": true,
42116         /**
42117          * @event beforeclose
42118          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42119          * @param {Roo.TabPanelItem} this
42120          * @param {Object} e Set cancel to true on this object to cancel the close.
42121          */
42122         "beforeclose": true,
42123         /**
42124          * @event close
42125          * Fires when this tab is closed.
42126          * @param {Roo.TabPanelItem} this
42127          */
42128          "close": true,
42129         /**
42130          * @event deactivate
42131          * Fires when this tab is no longer the active tab.
42132          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42133          * @param {Roo.TabPanelItem} this
42134          */
42135          "deactivate" : true
42136     });
42137     this.hidden = false;
42138
42139     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42140 };
42141
42142 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42143            {
42144     purgeListeners : function(){
42145        Roo.util.Observable.prototype.purgeListeners.call(this);
42146        this.el.removeAllListeners();
42147     },
42148     /**
42149      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42150      */
42151     show : function(){
42152         this.status_node.addClass("active");
42153         this.showAction();
42154         if(Roo.isOpera){
42155             this.tabPanel.stripWrap.repaint();
42156         }
42157         this.fireEvent("activate", this.tabPanel, this);
42158     },
42159
42160     /**
42161      * Returns true if this tab is the active tab.
42162      * @return {Boolean}
42163      */
42164     isActive : function(){
42165         return this.tabPanel.getActiveTab() == this;
42166     },
42167
42168     /**
42169      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42170      */
42171     hide : function(){
42172         this.status_node.removeClass("active");
42173         this.hideAction();
42174         this.fireEvent("deactivate", this.tabPanel, this);
42175     },
42176
42177     hideAction : function(){
42178         this.bodyEl.hide();
42179         this.bodyEl.setStyle("position", "absolute");
42180         this.bodyEl.setLeft("-20000px");
42181         this.bodyEl.setTop("-20000px");
42182     },
42183
42184     showAction : function(){
42185         this.bodyEl.setStyle("position", "relative");
42186         this.bodyEl.setTop("");
42187         this.bodyEl.setLeft("");
42188         this.bodyEl.show();
42189     },
42190
42191     /**
42192      * Set the tooltip for the tab.
42193      * @param {String} tooltip The tab's tooltip
42194      */
42195     setTooltip : function(text){
42196         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42197             this.textEl.dom.qtip = text;
42198             this.textEl.dom.removeAttribute('title');
42199         }else{
42200             this.textEl.dom.title = text;
42201         }
42202     },
42203
42204     onTabClick : function(e){
42205         e.preventDefault();
42206         this.tabPanel.activate(this.id);
42207     },
42208
42209     onTabMouseDown : function(e){
42210         e.preventDefault();
42211         this.tabPanel.activate(this.id);
42212     },
42213 /*
42214     getWidth : function(){
42215         return this.inner.getWidth();
42216     },
42217
42218     setWidth : function(width){
42219         var iwidth = width - this.linode.getPadding("lr");
42220         this.inner.setWidth(iwidth);
42221         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42222         this.linode.setWidth(width);
42223     },
42224 */
42225     /**
42226      * Show or hide the tab
42227      * @param {Boolean} hidden True to hide or false to show.
42228      */
42229     setHidden : function(hidden){
42230         this.hidden = hidden;
42231         this.linode.setStyle("display", hidden ? "none" : "");
42232     },
42233
42234     /**
42235      * Returns true if this tab is "hidden"
42236      * @return {Boolean}
42237      */
42238     isHidden : function(){
42239         return this.hidden;
42240     },
42241
42242     /**
42243      * Returns the text for this tab
42244      * @return {String}
42245      */
42246     getText : function(){
42247         return this.text;
42248     },
42249     /*
42250     autoSize : function(){
42251         //this.el.beginMeasure();
42252         this.textEl.setWidth(1);
42253         /*
42254          *  #2804 [new] Tabs in Roojs
42255          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42256          */
42257         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42258         //this.el.endMeasure();
42259     //},
42260
42261     /**
42262      * Sets the text for the tab (Note: this also sets the tooltip text)
42263      * @param {String} text The tab's text and tooltip
42264      */
42265     setText : function(text){
42266         this.text = text;
42267         this.textEl.update(text);
42268         this.setTooltip(text);
42269         //if(!this.tabPanel.resizeTabs){
42270         //    this.autoSize();
42271         //}
42272     },
42273     /**
42274      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42275      */
42276     activate : function(){
42277         this.tabPanel.activate(this.id);
42278     },
42279
42280     /**
42281      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42282      */
42283     disable : function(){
42284         if(this.tabPanel.active != this){
42285             this.disabled = true;
42286             this.status_node.addClass("disabled");
42287         }
42288     },
42289
42290     /**
42291      * Enables this TabPanelItem if it was previously disabled.
42292      */
42293     enable : function(){
42294         this.disabled = false;
42295         this.status_node.removeClass("disabled");
42296     },
42297
42298     /**
42299      * Sets the content for this TabPanelItem.
42300      * @param {String} content The content
42301      * @param {Boolean} loadScripts true to look for and load scripts
42302      */
42303     setContent : function(content, loadScripts){
42304         this.bodyEl.update(content, loadScripts);
42305     },
42306
42307     /**
42308      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42309      * @return {Roo.UpdateManager} The UpdateManager
42310      */
42311     getUpdateManager : function(){
42312         return this.bodyEl.getUpdateManager();
42313     },
42314
42315     /**
42316      * Set a URL to be used to load the content for this TabPanelItem.
42317      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42318      * @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)
42319      * @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)
42320      * @return {Roo.UpdateManager} The UpdateManager
42321      */
42322     setUrl : function(url, params, loadOnce){
42323         if(this.refreshDelegate){
42324             this.un('activate', this.refreshDelegate);
42325         }
42326         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42327         this.on("activate", this.refreshDelegate);
42328         return this.bodyEl.getUpdateManager();
42329     },
42330
42331     /** @private */
42332     _handleRefresh : function(url, params, loadOnce){
42333         if(!loadOnce || !this.loaded){
42334             var updater = this.bodyEl.getUpdateManager();
42335             updater.update(url, params, this._setLoaded.createDelegate(this));
42336         }
42337     },
42338
42339     /**
42340      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42341      *   Will fail silently if the setUrl method has not been called.
42342      *   This does not activate the panel, just updates its content.
42343      */
42344     refresh : function(){
42345         if(this.refreshDelegate){
42346            this.loaded = false;
42347            this.refreshDelegate();
42348         }
42349     },
42350
42351     /** @private */
42352     _setLoaded : function(){
42353         this.loaded = true;
42354     },
42355
42356     /** @private */
42357     closeClick : function(e){
42358         var o = {};
42359         e.stopEvent();
42360         this.fireEvent("beforeclose", this, o);
42361         if(o.cancel !== true){
42362             this.tabPanel.removeTab(this.id);
42363         }
42364     },
42365     /**
42366      * The text displayed in the tooltip for the close icon.
42367      * @type String
42368      */
42369     closeText : "Close this tab"
42370 });
42371 /**
42372 *    This script refer to:
42373 *    Title: International Telephone Input
42374 *    Author: Jack O'Connor
42375 *    Code version:  v12.1.12
42376 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42377 **/
42378
42379 Roo.bootstrap.PhoneInputData = function() {
42380     var d = [
42381       [
42382         "Afghanistan (‫افغانستان‬‎)",
42383         "af",
42384         "93"
42385       ],
42386       [
42387         "Albania (Shqipëri)",
42388         "al",
42389         "355"
42390       ],
42391       [
42392         "Algeria (‫الجزائر‬‎)",
42393         "dz",
42394         "213"
42395       ],
42396       [
42397         "American Samoa",
42398         "as",
42399         "1684"
42400       ],
42401       [
42402         "Andorra",
42403         "ad",
42404         "376"
42405       ],
42406       [
42407         "Angola",
42408         "ao",
42409         "244"
42410       ],
42411       [
42412         "Anguilla",
42413         "ai",
42414         "1264"
42415       ],
42416       [
42417         "Antigua and Barbuda",
42418         "ag",
42419         "1268"
42420       ],
42421       [
42422         "Argentina",
42423         "ar",
42424         "54"
42425       ],
42426       [
42427         "Armenia (Հայաստան)",
42428         "am",
42429         "374"
42430       ],
42431       [
42432         "Aruba",
42433         "aw",
42434         "297"
42435       ],
42436       [
42437         "Australia",
42438         "au",
42439         "61",
42440         0
42441       ],
42442       [
42443         "Austria (Österreich)",
42444         "at",
42445         "43"
42446       ],
42447       [
42448         "Azerbaijan (Azərbaycan)",
42449         "az",
42450         "994"
42451       ],
42452       [
42453         "Bahamas",
42454         "bs",
42455         "1242"
42456       ],
42457       [
42458         "Bahrain (‫البحرين‬‎)",
42459         "bh",
42460         "973"
42461       ],
42462       [
42463         "Bangladesh (বাংলাদেশ)",
42464         "bd",
42465         "880"
42466       ],
42467       [
42468         "Barbados",
42469         "bb",
42470         "1246"
42471       ],
42472       [
42473         "Belarus (Беларусь)",
42474         "by",
42475         "375"
42476       ],
42477       [
42478         "Belgium (België)",
42479         "be",
42480         "32"
42481       ],
42482       [
42483         "Belize",
42484         "bz",
42485         "501"
42486       ],
42487       [
42488         "Benin (Bénin)",
42489         "bj",
42490         "229"
42491       ],
42492       [
42493         "Bermuda",
42494         "bm",
42495         "1441"
42496       ],
42497       [
42498         "Bhutan (འབྲུག)",
42499         "bt",
42500         "975"
42501       ],
42502       [
42503         "Bolivia",
42504         "bo",
42505         "591"
42506       ],
42507       [
42508         "Bosnia and Herzegovina (Босна и Херцеговина)",
42509         "ba",
42510         "387"
42511       ],
42512       [
42513         "Botswana",
42514         "bw",
42515         "267"
42516       ],
42517       [
42518         "Brazil (Brasil)",
42519         "br",
42520         "55"
42521       ],
42522       [
42523         "British Indian Ocean Territory",
42524         "io",
42525         "246"
42526       ],
42527       [
42528         "British Virgin Islands",
42529         "vg",
42530         "1284"
42531       ],
42532       [
42533         "Brunei",
42534         "bn",
42535         "673"
42536       ],
42537       [
42538         "Bulgaria (България)",
42539         "bg",
42540         "359"
42541       ],
42542       [
42543         "Burkina Faso",
42544         "bf",
42545         "226"
42546       ],
42547       [
42548         "Burundi (Uburundi)",
42549         "bi",
42550         "257"
42551       ],
42552       [
42553         "Cambodia (កម្ពុជា)",
42554         "kh",
42555         "855"
42556       ],
42557       [
42558         "Cameroon (Cameroun)",
42559         "cm",
42560         "237"
42561       ],
42562       [
42563         "Canada",
42564         "ca",
42565         "1",
42566         1,
42567         ["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"]
42568       ],
42569       [
42570         "Cape Verde (Kabu Verdi)",
42571         "cv",
42572         "238"
42573       ],
42574       [
42575         "Caribbean Netherlands",
42576         "bq",
42577         "599",
42578         1
42579       ],
42580       [
42581         "Cayman Islands",
42582         "ky",
42583         "1345"
42584       ],
42585       [
42586         "Central African Republic (République centrafricaine)",
42587         "cf",
42588         "236"
42589       ],
42590       [
42591         "Chad (Tchad)",
42592         "td",
42593         "235"
42594       ],
42595       [
42596         "Chile",
42597         "cl",
42598         "56"
42599       ],
42600       [
42601         "China (中国)",
42602         "cn",
42603         "86"
42604       ],
42605       [
42606         "Christmas Island",
42607         "cx",
42608         "61",
42609         2
42610       ],
42611       [
42612         "Cocos (Keeling) Islands",
42613         "cc",
42614         "61",
42615         1
42616       ],
42617       [
42618         "Colombia",
42619         "co",
42620         "57"
42621       ],
42622       [
42623         "Comoros (‫جزر القمر‬‎)",
42624         "km",
42625         "269"
42626       ],
42627       [
42628         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42629         "cd",
42630         "243"
42631       ],
42632       [
42633         "Congo (Republic) (Congo-Brazzaville)",
42634         "cg",
42635         "242"
42636       ],
42637       [
42638         "Cook Islands",
42639         "ck",
42640         "682"
42641       ],
42642       [
42643         "Costa Rica",
42644         "cr",
42645         "506"
42646       ],
42647       [
42648         "Côte d’Ivoire",
42649         "ci",
42650         "225"
42651       ],
42652       [
42653         "Croatia (Hrvatska)",
42654         "hr",
42655         "385"
42656       ],
42657       [
42658         "Cuba",
42659         "cu",
42660         "53"
42661       ],
42662       [
42663         "Curaçao",
42664         "cw",
42665         "599",
42666         0
42667       ],
42668       [
42669         "Cyprus (Κύπρος)",
42670         "cy",
42671         "357"
42672       ],
42673       [
42674         "Czech Republic (Česká republika)",
42675         "cz",
42676         "420"
42677       ],
42678       [
42679         "Denmark (Danmark)",
42680         "dk",
42681         "45"
42682       ],
42683       [
42684         "Djibouti",
42685         "dj",
42686         "253"
42687       ],
42688       [
42689         "Dominica",
42690         "dm",
42691         "1767"
42692       ],
42693       [
42694         "Dominican Republic (República Dominicana)",
42695         "do",
42696         "1",
42697         2,
42698         ["809", "829", "849"]
42699       ],
42700       [
42701         "Ecuador",
42702         "ec",
42703         "593"
42704       ],
42705       [
42706         "Egypt (‫مصر‬‎)",
42707         "eg",
42708         "20"
42709       ],
42710       [
42711         "El Salvador",
42712         "sv",
42713         "503"
42714       ],
42715       [
42716         "Equatorial Guinea (Guinea Ecuatorial)",
42717         "gq",
42718         "240"
42719       ],
42720       [
42721         "Eritrea",
42722         "er",
42723         "291"
42724       ],
42725       [
42726         "Estonia (Eesti)",
42727         "ee",
42728         "372"
42729       ],
42730       [
42731         "Ethiopia",
42732         "et",
42733         "251"
42734       ],
42735       [
42736         "Falkland Islands (Islas Malvinas)",
42737         "fk",
42738         "500"
42739       ],
42740       [
42741         "Faroe Islands (Føroyar)",
42742         "fo",
42743         "298"
42744       ],
42745       [
42746         "Fiji",
42747         "fj",
42748         "679"
42749       ],
42750       [
42751         "Finland (Suomi)",
42752         "fi",
42753         "358",
42754         0
42755       ],
42756       [
42757         "France",
42758         "fr",
42759         "33"
42760       ],
42761       [
42762         "French Guiana (Guyane française)",
42763         "gf",
42764         "594"
42765       ],
42766       [
42767         "French Polynesia (Polynésie française)",
42768         "pf",
42769         "689"
42770       ],
42771       [
42772         "Gabon",
42773         "ga",
42774         "241"
42775       ],
42776       [
42777         "Gambia",
42778         "gm",
42779         "220"
42780       ],
42781       [
42782         "Georgia (საქართველო)",
42783         "ge",
42784         "995"
42785       ],
42786       [
42787         "Germany (Deutschland)",
42788         "de",
42789         "49"
42790       ],
42791       [
42792         "Ghana (Gaana)",
42793         "gh",
42794         "233"
42795       ],
42796       [
42797         "Gibraltar",
42798         "gi",
42799         "350"
42800       ],
42801       [
42802         "Greece (Ελλάδα)",
42803         "gr",
42804         "30"
42805       ],
42806       [
42807         "Greenland (Kalaallit Nunaat)",
42808         "gl",
42809         "299"
42810       ],
42811       [
42812         "Grenada",
42813         "gd",
42814         "1473"
42815       ],
42816       [
42817         "Guadeloupe",
42818         "gp",
42819         "590",
42820         0
42821       ],
42822       [
42823         "Guam",
42824         "gu",
42825         "1671"
42826       ],
42827       [
42828         "Guatemala",
42829         "gt",
42830         "502"
42831       ],
42832       [
42833         "Guernsey",
42834         "gg",
42835         "44",
42836         1
42837       ],
42838       [
42839         "Guinea (Guinée)",
42840         "gn",
42841         "224"
42842       ],
42843       [
42844         "Guinea-Bissau (Guiné Bissau)",
42845         "gw",
42846         "245"
42847       ],
42848       [
42849         "Guyana",
42850         "gy",
42851         "592"
42852       ],
42853       [
42854         "Haiti",
42855         "ht",
42856         "509"
42857       ],
42858       [
42859         "Honduras",
42860         "hn",
42861         "504"
42862       ],
42863       [
42864         "Hong Kong (香港)",
42865         "hk",
42866         "852"
42867       ],
42868       [
42869         "Hungary (Magyarország)",
42870         "hu",
42871         "36"
42872       ],
42873       [
42874         "Iceland (Ísland)",
42875         "is",
42876         "354"
42877       ],
42878       [
42879         "India (भारत)",
42880         "in",
42881         "91"
42882       ],
42883       [
42884         "Indonesia",
42885         "id",
42886         "62"
42887       ],
42888       [
42889         "Iran (‫ایران‬‎)",
42890         "ir",
42891         "98"
42892       ],
42893       [
42894         "Iraq (‫العراق‬‎)",
42895         "iq",
42896         "964"
42897       ],
42898       [
42899         "Ireland",
42900         "ie",
42901         "353"
42902       ],
42903       [
42904         "Isle of Man",
42905         "im",
42906         "44",
42907         2
42908       ],
42909       [
42910         "Israel (‫ישראל‬‎)",
42911         "il",
42912         "972"
42913       ],
42914       [
42915         "Italy (Italia)",
42916         "it",
42917         "39",
42918         0
42919       ],
42920       [
42921         "Jamaica",
42922         "jm",
42923         "1876"
42924       ],
42925       [
42926         "Japan (日本)",
42927         "jp",
42928         "81"
42929       ],
42930       [
42931         "Jersey",
42932         "je",
42933         "44",
42934         3
42935       ],
42936       [
42937         "Jordan (‫الأردن‬‎)",
42938         "jo",
42939         "962"
42940       ],
42941       [
42942         "Kazakhstan (Казахстан)",
42943         "kz",
42944         "7",
42945         1
42946       ],
42947       [
42948         "Kenya",
42949         "ke",
42950         "254"
42951       ],
42952       [
42953         "Kiribati",
42954         "ki",
42955         "686"
42956       ],
42957       [
42958         "Kosovo",
42959         "xk",
42960         "383"
42961       ],
42962       [
42963         "Kuwait (‫الكويت‬‎)",
42964         "kw",
42965         "965"
42966       ],
42967       [
42968         "Kyrgyzstan (Кыргызстан)",
42969         "kg",
42970         "996"
42971       ],
42972       [
42973         "Laos (ລາວ)",
42974         "la",
42975         "856"
42976       ],
42977       [
42978         "Latvia (Latvija)",
42979         "lv",
42980         "371"
42981       ],
42982       [
42983         "Lebanon (‫لبنان‬‎)",
42984         "lb",
42985         "961"
42986       ],
42987       [
42988         "Lesotho",
42989         "ls",
42990         "266"
42991       ],
42992       [
42993         "Liberia",
42994         "lr",
42995         "231"
42996       ],
42997       [
42998         "Libya (‫ليبيا‬‎)",
42999         "ly",
43000         "218"
43001       ],
43002       [
43003         "Liechtenstein",
43004         "li",
43005         "423"
43006       ],
43007       [
43008         "Lithuania (Lietuva)",
43009         "lt",
43010         "370"
43011       ],
43012       [
43013         "Luxembourg",
43014         "lu",
43015         "352"
43016       ],
43017       [
43018         "Macau (澳門)",
43019         "mo",
43020         "853"
43021       ],
43022       [
43023         "Macedonia (FYROM) (Македонија)",
43024         "mk",
43025         "389"
43026       ],
43027       [
43028         "Madagascar (Madagasikara)",
43029         "mg",
43030         "261"
43031       ],
43032       [
43033         "Malawi",
43034         "mw",
43035         "265"
43036       ],
43037       [
43038         "Malaysia",
43039         "my",
43040         "60"
43041       ],
43042       [
43043         "Maldives",
43044         "mv",
43045         "960"
43046       ],
43047       [
43048         "Mali",
43049         "ml",
43050         "223"
43051       ],
43052       [
43053         "Malta",
43054         "mt",
43055         "356"
43056       ],
43057       [
43058         "Marshall Islands",
43059         "mh",
43060         "692"
43061       ],
43062       [
43063         "Martinique",
43064         "mq",
43065         "596"
43066       ],
43067       [
43068         "Mauritania (‫موريتانيا‬‎)",
43069         "mr",
43070         "222"
43071       ],
43072       [
43073         "Mauritius (Moris)",
43074         "mu",
43075         "230"
43076       ],
43077       [
43078         "Mayotte",
43079         "yt",
43080         "262",
43081         1
43082       ],
43083       [
43084         "Mexico (México)",
43085         "mx",
43086         "52"
43087       ],
43088       [
43089         "Micronesia",
43090         "fm",
43091         "691"
43092       ],
43093       [
43094         "Moldova (Republica Moldova)",
43095         "md",
43096         "373"
43097       ],
43098       [
43099         "Monaco",
43100         "mc",
43101         "377"
43102       ],
43103       [
43104         "Mongolia (Монгол)",
43105         "mn",
43106         "976"
43107       ],
43108       [
43109         "Montenegro (Crna Gora)",
43110         "me",
43111         "382"
43112       ],
43113       [
43114         "Montserrat",
43115         "ms",
43116         "1664"
43117       ],
43118       [
43119         "Morocco (‫المغرب‬‎)",
43120         "ma",
43121         "212",
43122         0
43123       ],
43124       [
43125         "Mozambique (Moçambique)",
43126         "mz",
43127         "258"
43128       ],
43129       [
43130         "Myanmar (Burma) (မြန်မာ)",
43131         "mm",
43132         "95"
43133       ],
43134       [
43135         "Namibia (Namibië)",
43136         "na",
43137         "264"
43138       ],
43139       [
43140         "Nauru",
43141         "nr",
43142         "674"
43143       ],
43144       [
43145         "Nepal (नेपाल)",
43146         "np",
43147         "977"
43148       ],
43149       [
43150         "Netherlands (Nederland)",
43151         "nl",
43152         "31"
43153       ],
43154       [
43155         "New Caledonia (Nouvelle-Calédonie)",
43156         "nc",
43157         "687"
43158       ],
43159       [
43160         "New Zealand",
43161         "nz",
43162         "64"
43163       ],
43164       [
43165         "Nicaragua",
43166         "ni",
43167         "505"
43168       ],
43169       [
43170         "Niger (Nijar)",
43171         "ne",
43172         "227"
43173       ],
43174       [
43175         "Nigeria",
43176         "ng",
43177         "234"
43178       ],
43179       [
43180         "Niue",
43181         "nu",
43182         "683"
43183       ],
43184       [
43185         "Norfolk Island",
43186         "nf",
43187         "672"
43188       ],
43189       [
43190         "North Korea (조선 민주주의 인민 공화국)",
43191         "kp",
43192         "850"
43193       ],
43194       [
43195         "Northern Mariana Islands",
43196         "mp",
43197         "1670"
43198       ],
43199       [
43200         "Norway (Norge)",
43201         "no",
43202         "47",
43203         0
43204       ],
43205       [
43206         "Oman (‫عُمان‬‎)",
43207         "om",
43208         "968"
43209       ],
43210       [
43211         "Pakistan (‫پاکستان‬‎)",
43212         "pk",
43213         "92"
43214       ],
43215       [
43216         "Palau",
43217         "pw",
43218         "680"
43219       ],
43220       [
43221         "Palestine (‫فلسطين‬‎)",
43222         "ps",
43223         "970"
43224       ],
43225       [
43226         "Panama (Panamá)",
43227         "pa",
43228         "507"
43229       ],
43230       [
43231         "Papua New Guinea",
43232         "pg",
43233         "675"
43234       ],
43235       [
43236         "Paraguay",
43237         "py",
43238         "595"
43239       ],
43240       [
43241         "Peru (Perú)",
43242         "pe",
43243         "51"
43244       ],
43245       [
43246         "Philippines",
43247         "ph",
43248         "63"
43249       ],
43250       [
43251         "Poland (Polska)",
43252         "pl",
43253         "48"
43254       ],
43255       [
43256         "Portugal",
43257         "pt",
43258         "351"
43259       ],
43260       [
43261         "Puerto Rico",
43262         "pr",
43263         "1",
43264         3,
43265         ["787", "939"]
43266       ],
43267       [
43268         "Qatar (‫قطر‬‎)",
43269         "qa",
43270         "974"
43271       ],
43272       [
43273         "Réunion (La Réunion)",
43274         "re",
43275         "262",
43276         0
43277       ],
43278       [
43279         "Romania (România)",
43280         "ro",
43281         "40"
43282       ],
43283       [
43284         "Russia (Россия)",
43285         "ru",
43286         "7",
43287         0
43288       ],
43289       [
43290         "Rwanda",
43291         "rw",
43292         "250"
43293       ],
43294       [
43295         "Saint Barthélemy",
43296         "bl",
43297         "590",
43298         1
43299       ],
43300       [
43301         "Saint Helena",
43302         "sh",
43303         "290"
43304       ],
43305       [
43306         "Saint Kitts and Nevis",
43307         "kn",
43308         "1869"
43309       ],
43310       [
43311         "Saint Lucia",
43312         "lc",
43313         "1758"
43314       ],
43315       [
43316         "Saint Martin (Saint-Martin (partie française))",
43317         "mf",
43318         "590",
43319         2
43320       ],
43321       [
43322         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43323         "pm",
43324         "508"
43325       ],
43326       [
43327         "Saint Vincent and the Grenadines",
43328         "vc",
43329         "1784"
43330       ],
43331       [
43332         "Samoa",
43333         "ws",
43334         "685"
43335       ],
43336       [
43337         "San Marino",
43338         "sm",
43339         "378"
43340       ],
43341       [
43342         "São Tomé and Príncipe (São Tomé e Príncipe)",
43343         "st",
43344         "239"
43345       ],
43346       [
43347         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43348         "sa",
43349         "966"
43350       ],
43351       [
43352         "Senegal (Sénégal)",
43353         "sn",
43354         "221"
43355       ],
43356       [
43357         "Serbia (Србија)",
43358         "rs",
43359         "381"
43360       ],
43361       [
43362         "Seychelles",
43363         "sc",
43364         "248"
43365       ],
43366       [
43367         "Sierra Leone",
43368         "sl",
43369         "232"
43370       ],
43371       [
43372         "Singapore",
43373         "sg",
43374         "65"
43375       ],
43376       [
43377         "Sint Maarten",
43378         "sx",
43379         "1721"
43380       ],
43381       [
43382         "Slovakia (Slovensko)",
43383         "sk",
43384         "421"
43385       ],
43386       [
43387         "Slovenia (Slovenija)",
43388         "si",
43389         "386"
43390       ],
43391       [
43392         "Solomon Islands",
43393         "sb",
43394         "677"
43395       ],
43396       [
43397         "Somalia (Soomaaliya)",
43398         "so",
43399         "252"
43400       ],
43401       [
43402         "South Africa",
43403         "za",
43404         "27"
43405       ],
43406       [
43407         "South Korea (대한민국)",
43408         "kr",
43409         "82"
43410       ],
43411       [
43412         "South Sudan (‫جنوب السودان‬‎)",
43413         "ss",
43414         "211"
43415       ],
43416       [
43417         "Spain (España)",
43418         "es",
43419         "34"
43420       ],
43421       [
43422         "Sri Lanka (ශ්‍රී ලංකාව)",
43423         "lk",
43424         "94"
43425       ],
43426       [
43427         "Sudan (‫السودان‬‎)",
43428         "sd",
43429         "249"
43430       ],
43431       [
43432         "Suriname",
43433         "sr",
43434         "597"
43435       ],
43436       [
43437         "Svalbard and Jan Mayen",
43438         "sj",
43439         "47",
43440         1
43441       ],
43442       [
43443         "Swaziland",
43444         "sz",
43445         "268"
43446       ],
43447       [
43448         "Sweden (Sverige)",
43449         "se",
43450         "46"
43451       ],
43452       [
43453         "Switzerland (Schweiz)",
43454         "ch",
43455         "41"
43456       ],
43457       [
43458         "Syria (‫سوريا‬‎)",
43459         "sy",
43460         "963"
43461       ],
43462       [
43463         "Taiwan (台灣)",
43464         "tw",
43465         "886"
43466       ],
43467       [
43468         "Tajikistan",
43469         "tj",
43470         "992"
43471       ],
43472       [
43473         "Tanzania",
43474         "tz",
43475         "255"
43476       ],
43477       [
43478         "Thailand (ไทย)",
43479         "th",
43480         "66"
43481       ],
43482       [
43483         "Timor-Leste",
43484         "tl",
43485         "670"
43486       ],
43487       [
43488         "Togo",
43489         "tg",
43490         "228"
43491       ],
43492       [
43493         "Tokelau",
43494         "tk",
43495         "690"
43496       ],
43497       [
43498         "Tonga",
43499         "to",
43500         "676"
43501       ],
43502       [
43503         "Trinidad and Tobago",
43504         "tt",
43505         "1868"
43506       ],
43507       [
43508         "Tunisia (‫تونس‬‎)",
43509         "tn",
43510         "216"
43511       ],
43512       [
43513         "Turkey (Türkiye)",
43514         "tr",
43515         "90"
43516       ],
43517       [
43518         "Turkmenistan",
43519         "tm",
43520         "993"
43521       ],
43522       [
43523         "Turks and Caicos Islands",
43524         "tc",
43525         "1649"
43526       ],
43527       [
43528         "Tuvalu",
43529         "tv",
43530         "688"
43531       ],
43532       [
43533         "U.S. Virgin Islands",
43534         "vi",
43535         "1340"
43536       ],
43537       [
43538         "Uganda",
43539         "ug",
43540         "256"
43541       ],
43542       [
43543         "Ukraine (Україна)",
43544         "ua",
43545         "380"
43546       ],
43547       [
43548         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43549         "ae",
43550         "971"
43551       ],
43552       [
43553         "United Kingdom",
43554         "gb",
43555         "44",
43556         0
43557       ],
43558       [
43559         "United States",
43560         "us",
43561         "1",
43562         0
43563       ],
43564       [
43565         "Uruguay",
43566         "uy",
43567         "598"
43568       ],
43569       [
43570         "Uzbekistan (Oʻzbekiston)",
43571         "uz",
43572         "998"
43573       ],
43574       [
43575         "Vanuatu",
43576         "vu",
43577         "678"
43578       ],
43579       [
43580         "Vatican City (Città del Vaticano)",
43581         "va",
43582         "39",
43583         1
43584       ],
43585       [
43586         "Venezuela",
43587         "ve",
43588         "58"
43589       ],
43590       [
43591         "Vietnam (Việt Nam)",
43592         "vn",
43593         "84"
43594       ],
43595       [
43596         "Wallis and Futuna (Wallis-et-Futuna)",
43597         "wf",
43598         "681"
43599       ],
43600       [
43601         "Western Sahara (‫الصحراء الغربية‬‎)",
43602         "eh",
43603         "212",
43604         1
43605       ],
43606       [
43607         "Yemen (‫اليمن‬‎)",
43608         "ye",
43609         "967"
43610       ],
43611       [
43612         "Zambia",
43613         "zm",
43614         "260"
43615       ],
43616       [
43617         "Zimbabwe",
43618         "zw",
43619         "263"
43620       ],
43621       [
43622         "Åland Islands",
43623         "ax",
43624         "358",
43625         1
43626       ]
43627   ];
43628   
43629   return d;
43630 }/**
43631 *    This script refer to:
43632 *    Title: International Telephone Input
43633 *    Author: Jack O'Connor
43634 *    Code version:  v12.1.12
43635 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43636 **/
43637
43638 /**
43639  * @class Roo.bootstrap.PhoneInput
43640  * @extends Roo.bootstrap.TriggerField
43641  * An input with International dial-code selection
43642  
43643  * @cfg {String} defaultDialCode default '+852'
43644  * @cfg {Array} preferedCountries default []
43645   
43646  * @constructor
43647  * Create a new PhoneInput.
43648  * @param {Object} config Configuration options
43649  */
43650
43651 Roo.bootstrap.PhoneInput = function(config) {
43652     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43653 };
43654
43655 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43656         /**
43657         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43658         */
43659         listWidth: undefined,
43660         
43661         selectedClass: 'active',
43662         
43663         invalidClass : "has-warning",
43664         
43665         validClass: 'has-success',
43666         
43667         allowed: '0123456789',
43668         
43669         max_length: 15,
43670         
43671         /**
43672          * @cfg {String} defaultDialCode The default dial code when initializing the input
43673          */
43674         defaultDialCode: '+852',
43675         
43676         /**
43677          * @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
43678          */
43679         preferedCountries: false,
43680         
43681         getAutoCreate : function()
43682         {
43683             var data = Roo.bootstrap.PhoneInputData();
43684             var align = this.labelAlign || this.parentLabelAlign();
43685             var id = Roo.id();
43686             
43687             this.allCountries = [];
43688             this.dialCodeMapping = [];
43689             
43690             for (var i = 0; i < data.length; i++) {
43691               var c = data[i];
43692               this.allCountries[i] = {
43693                 name: c[0],
43694                 iso2: c[1],
43695                 dialCode: c[2],
43696                 priority: c[3] || 0,
43697                 areaCodes: c[4] || null
43698               };
43699               this.dialCodeMapping[c[2]] = {
43700                   name: c[0],
43701                   iso2: c[1],
43702                   priority: c[3] || 0,
43703                   areaCodes: c[4] || null
43704               };
43705             }
43706             
43707             var cfg = {
43708                 cls: 'form-group',
43709                 cn: []
43710             };
43711             
43712             var input =  {
43713                 tag: 'input',
43714                 id : id,
43715                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43716                 maxlength: this.max_length,
43717                 cls : 'form-control tel-input',
43718                 autocomplete: 'new-password'
43719             };
43720             
43721             var hiddenInput = {
43722                 tag: 'input',
43723                 type: 'hidden',
43724                 cls: 'hidden-tel-input'
43725             };
43726             
43727             if (this.name) {
43728                 hiddenInput.name = this.name;
43729             }
43730             
43731             if (this.disabled) {
43732                 input.disabled = true;
43733             }
43734             
43735             var flag_container = {
43736                 tag: 'div',
43737                 cls: 'flag-box',
43738                 cn: [
43739                     {
43740                         tag: 'div',
43741                         cls: 'flag'
43742                     },
43743                     {
43744                         tag: 'div',
43745                         cls: 'caret'
43746                     }
43747                 ]
43748             };
43749             
43750             var box = {
43751                 tag: 'div',
43752                 cls: this.hasFeedback ? 'has-feedback' : '',
43753                 cn: [
43754                     hiddenInput,
43755                     input,
43756                     {
43757                         tag: 'input',
43758                         cls: 'dial-code-holder',
43759                         disabled: true
43760                     }
43761                 ]
43762             };
43763             
43764             var container = {
43765                 cls: 'roo-select2-container input-group',
43766                 cn: [
43767                     flag_container,
43768                     box
43769                 ]
43770             };
43771             
43772             if (this.fieldLabel.length) {
43773                 var indicator = {
43774                     tag: 'i',
43775                     tooltip: 'This field is required'
43776                 };
43777                 
43778                 var label = {
43779                     tag: 'label',
43780                     'for':  id,
43781                     cls: 'control-label',
43782                     cn: []
43783                 };
43784                 
43785                 var label_text = {
43786                     tag: 'span',
43787                     html: this.fieldLabel
43788                 };
43789                 
43790                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43791                 label.cn = [
43792                     indicator,
43793                     label_text
43794                 ];
43795                 
43796                 if(this.indicatorpos == 'right') {
43797                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43798                     label.cn = [
43799                         label_text,
43800                         indicator
43801                     ];
43802                 }
43803                 
43804                 if(align == 'left') {
43805                     container = {
43806                         tag: 'div',
43807                         cn: [
43808                             container
43809                         ]
43810                     };
43811                     
43812                     if(this.labelWidth > 12){
43813                         label.style = "width: " + this.labelWidth + 'px';
43814                     }
43815                     if(this.labelWidth < 13 && this.labelmd == 0){
43816                         this.labelmd = this.labelWidth;
43817                     }
43818                     if(this.labellg > 0){
43819                         label.cls += ' col-lg-' + this.labellg;
43820                         input.cls += ' col-lg-' + (12 - this.labellg);
43821                     }
43822                     if(this.labelmd > 0){
43823                         label.cls += ' col-md-' + this.labelmd;
43824                         container.cls += ' col-md-' + (12 - this.labelmd);
43825                     }
43826                     if(this.labelsm > 0){
43827                         label.cls += ' col-sm-' + this.labelsm;
43828                         container.cls += ' col-sm-' + (12 - this.labelsm);
43829                     }
43830                     if(this.labelxs > 0){
43831                         label.cls += ' col-xs-' + this.labelxs;
43832                         container.cls += ' col-xs-' + (12 - this.labelxs);
43833                     }
43834                 }
43835             }
43836             
43837             cfg.cn = [
43838                 label,
43839                 container
43840             ];
43841             
43842             var settings = this;
43843             
43844             ['xs','sm','md','lg'].map(function(size){
43845                 if (settings[size]) {
43846                     cfg.cls += ' col-' + size + '-' + settings[size];
43847                 }
43848             });
43849             
43850             this.store = new Roo.data.Store({
43851                 proxy : new Roo.data.MemoryProxy({}),
43852                 reader : new Roo.data.JsonReader({
43853                     fields : [
43854                         {
43855                             'name' : 'name',
43856                             'type' : 'string'
43857                         },
43858                         {
43859                             'name' : 'iso2',
43860                             'type' : 'string'
43861                         },
43862                         {
43863                             'name' : 'dialCode',
43864                             'type' : 'string'
43865                         },
43866                         {
43867                             'name' : 'priority',
43868                             'type' : 'string'
43869                         },
43870                         {
43871                             'name' : 'areaCodes',
43872                             'type' : 'string'
43873                         }
43874                     ]
43875                 })
43876             });
43877             
43878             if(!this.preferedCountries) {
43879                 this.preferedCountries = [
43880                     'hk',
43881                     'gb',
43882                     'us'
43883                 ];
43884             }
43885             
43886             var p = this.preferedCountries.reverse();
43887             
43888             if(p) {
43889                 for (var i = 0; i < p.length; i++) {
43890                     for (var j = 0; j < this.allCountries.length; j++) {
43891                         if(this.allCountries[j].iso2 == p[i]) {
43892                             var t = this.allCountries[j];
43893                             this.allCountries.splice(j,1);
43894                             this.allCountries.unshift(t);
43895                         }
43896                     } 
43897                 }
43898             }
43899             
43900             this.store.proxy.data = {
43901                 success: true,
43902                 data: this.allCountries
43903             };
43904             
43905             return cfg;
43906         },
43907         
43908         initEvents : function()
43909         {
43910             this.createList();
43911             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43912             
43913             this.indicator = this.indicatorEl();
43914             this.flag = this.flagEl();
43915             this.dialCodeHolder = this.dialCodeHolderEl();
43916             
43917             this.trigger = this.el.select('div.flag-box',true).first();
43918             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43919             
43920             var _this = this;
43921             
43922             (function(){
43923                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43924                 _this.list.setWidth(lw);
43925             }).defer(100);
43926             
43927             this.list.on('mouseover', this.onViewOver, this);
43928             this.list.on('mousemove', this.onViewMove, this);
43929             this.inputEl().on("keyup", this.onKeyUp, this);
43930             this.inputEl().on("keypress", this.onKeyPress, this);
43931             
43932             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43933
43934             this.view = new Roo.View(this.list, this.tpl, {
43935                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43936             });
43937             
43938             this.view.on('click', this.onViewClick, this);
43939             this.setValue(this.defaultDialCode);
43940         },
43941         
43942         onTriggerClick : function(e)
43943         {
43944             Roo.log('trigger click');
43945             if(this.disabled){
43946                 return;
43947             }
43948             
43949             if(this.isExpanded()){
43950                 this.collapse();
43951                 this.hasFocus = false;
43952             }else {
43953                 this.store.load({});
43954                 this.hasFocus = true;
43955                 this.expand();
43956             }
43957         },
43958         
43959         isExpanded : function()
43960         {
43961             return this.list.isVisible();
43962         },
43963         
43964         collapse : function()
43965         {
43966             if(!this.isExpanded()){
43967                 return;
43968             }
43969             this.list.hide();
43970             Roo.get(document).un('mousedown', this.collapseIf, this);
43971             Roo.get(document).un('mousewheel', this.collapseIf, this);
43972             this.fireEvent('collapse', this);
43973             this.validate();
43974         },
43975         
43976         expand : function()
43977         {
43978             Roo.log('expand');
43979
43980             if(this.isExpanded() || !this.hasFocus){
43981                 return;
43982             }
43983             
43984             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43985             this.list.setWidth(lw);
43986             
43987             this.list.show();
43988             this.restrictHeight();
43989             
43990             Roo.get(document).on('mousedown', this.collapseIf, this);
43991             Roo.get(document).on('mousewheel', this.collapseIf, this);
43992             
43993             this.fireEvent('expand', this);
43994         },
43995         
43996         restrictHeight : function()
43997         {
43998             this.list.alignTo(this.inputEl(), this.listAlign);
43999             this.list.alignTo(this.inputEl(), this.listAlign);
44000         },
44001         
44002         onViewOver : function(e, t)
44003         {
44004             if(this.inKeyMode){
44005                 return;
44006             }
44007             var item = this.view.findItemFromChild(t);
44008             
44009             if(item){
44010                 var index = this.view.indexOf(item);
44011                 this.select(index, false);
44012             }
44013         },
44014
44015         // private
44016         onViewClick : function(view, doFocus, el, e)
44017         {
44018             var index = this.view.getSelectedIndexes()[0];
44019             
44020             var r = this.store.getAt(index);
44021             
44022             if(r){
44023                 this.onSelect(r, index);
44024             }
44025             if(doFocus !== false && !this.blockFocus){
44026                 this.inputEl().focus();
44027             }
44028         },
44029         
44030         onViewMove : function(e, t)
44031         {
44032             this.inKeyMode = false;
44033         },
44034         
44035         select : function(index, scrollIntoView)
44036         {
44037             this.selectedIndex = index;
44038             this.view.select(index);
44039             if(scrollIntoView !== false){
44040                 var el = this.view.getNode(index);
44041                 if(el){
44042                     this.list.scrollChildIntoView(el, false);
44043                 }
44044             }
44045         },
44046         
44047         createList : function()
44048         {
44049             this.list = Roo.get(document.body).createChild({
44050                 tag: 'ul',
44051                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44052                 style: 'display:none'
44053             });
44054             
44055             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44056         },
44057         
44058         collapseIf : function(e)
44059         {
44060             var in_combo  = e.within(this.el);
44061             var in_list =  e.within(this.list);
44062             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44063             
44064             if (in_combo || in_list || is_list) {
44065                 return;
44066             }
44067             this.collapse();
44068         },
44069         
44070         onSelect : function(record, index)
44071         {
44072             if(this.fireEvent('beforeselect', this, record, index) !== false){
44073                 
44074                 this.setFlagClass(record.data.iso2);
44075                 this.setDialCode(record.data.dialCode);
44076                 this.hasFocus = false;
44077                 this.collapse();
44078                 this.fireEvent('select', this, record, index);
44079             }
44080         },
44081         
44082         flagEl : function()
44083         {
44084             var flag = this.el.select('div.flag',true).first();
44085             if(!flag){
44086                 return false;
44087             }
44088             return flag;
44089         },
44090         
44091         dialCodeHolderEl : function()
44092         {
44093             var d = this.el.select('input.dial-code-holder',true).first();
44094             if(!d){
44095                 return false;
44096             }
44097             return d;
44098         },
44099         
44100         setDialCode : function(v)
44101         {
44102             this.dialCodeHolder.dom.value = '+'+v;
44103         },
44104         
44105         setFlagClass : function(n)
44106         {
44107             this.flag.dom.className = 'flag '+n;
44108         },
44109         
44110         getValue : function()
44111         {
44112             var v = this.inputEl().getValue();
44113             if(this.dialCodeHolder) {
44114                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44115             }
44116             return v;
44117         },
44118         
44119         setValue : function(v)
44120         {
44121             var d = this.getDialCode(v);
44122             
44123             //invalid dial code
44124             if(v.length == 0 || !d || d.length == 0) {
44125                 if(this.rendered){
44126                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44127                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44128                 }
44129                 return;
44130             }
44131             
44132             //valid dial code
44133             this.setFlagClass(this.dialCodeMapping[d].iso2);
44134             this.setDialCode(d);
44135             this.inputEl().dom.value = v.replace('+'+d,'');
44136             this.hiddenEl().dom.value = this.getValue();
44137             
44138             this.validate();
44139         },
44140         
44141         getDialCode : function(v)
44142         {
44143             v = v ||  '';
44144             
44145             if (v.length == 0) {
44146                 return this.dialCodeHolder.dom.value;
44147             }
44148             
44149             var dialCode = "";
44150             if (v.charAt(0) != "+") {
44151                 return false;
44152             }
44153             var numericChars = "";
44154             for (var i = 1; i < v.length; i++) {
44155               var c = v.charAt(i);
44156               if (!isNaN(c)) {
44157                 numericChars += c;
44158                 if (this.dialCodeMapping[numericChars]) {
44159                   dialCode = v.substr(1, i);
44160                 }
44161                 if (numericChars.length == 4) {
44162                   break;
44163                 }
44164               }
44165             }
44166             return dialCode;
44167         },
44168         
44169         reset : function()
44170         {
44171             this.setValue(this.defaultDialCode);
44172             this.validate();
44173         },
44174         
44175         hiddenEl : function()
44176         {
44177             return this.el.select('input.hidden-tel-input',true).first();
44178         },
44179         
44180         // after setting val
44181         onKeyUp : function(e){
44182             this.setValue(this.getValue());
44183         },
44184         
44185         onKeyPress : function(e){
44186             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44187                 e.stopEvent();
44188             }
44189         }
44190         
44191 });
44192 /**
44193  * @class Roo.bootstrap.MoneyField
44194  * @extends Roo.bootstrap.ComboBox
44195  * Bootstrap MoneyField class
44196  * 
44197  * @constructor
44198  * Create a new MoneyField.
44199  * @param {Object} config Configuration options
44200  */
44201
44202 Roo.bootstrap.MoneyField = function(config) {
44203     
44204     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44205     
44206 };
44207
44208 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44209     
44210     /**
44211      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44212      */
44213     allowDecimals : true,
44214     /**
44215      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44216      */
44217     decimalSeparator : ".",
44218     /**
44219      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44220      */
44221     decimalPrecision : 0,
44222     /**
44223      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44224      */
44225     allowNegative : true,
44226     /**
44227      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44228      */
44229     allowZero: true,
44230     /**
44231      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44232      */
44233     minValue : Number.NEGATIVE_INFINITY,
44234     /**
44235      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44236      */
44237     maxValue : Number.MAX_VALUE,
44238     /**
44239      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44240      */
44241     minText : "The minimum value for this field is {0}",
44242     /**
44243      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44244      */
44245     maxText : "The maximum value for this field is {0}",
44246     /**
44247      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44248      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44249      */
44250     nanText : "{0} is not a valid number",
44251     /**
44252      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44253      */
44254     castInt : true,
44255     /**
44256      * @cfg {String} defaults currency of the MoneyField
44257      * value should be in lkey
44258      */
44259     defaultCurrency : false,
44260     /**
44261      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44262      */
44263     thousandsDelimiter : false,
44264     /**
44265      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44266      */
44267     max_length: false,
44268     
44269     inputlg : 9,
44270     inputmd : 9,
44271     inputsm : 9,
44272     inputxs : 6,
44273     
44274     store : false,
44275     
44276     getAutoCreate : function()
44277     {
44278         var align = this.labelAlign || this.parentLabelAlign();
44279         
44280         var id = Roo.id();
44281
44282         var cfg = {
44283             cls: 'form-group',
44284             cn: []
44285         };
44286
44287         var input =  {
44288             tag: 'input',
44289             id : id,
44290             cls : 'form-control roo-money-amount-input',
44291             autocomplete: 'new-password'
44292         };
44293         
44294         var hiddenInput = {
44295             tag: 'input',
44296             type: 'hidden',
44297             id: Roo.id(),
44298             cls: 'hidden-number-input'
44299         };
44300         
44301         if(this.max_length) {
44302             input.maxlength = this.max_length; 
44303         }
44304         
44305         if (this.name) {
44306             hiddenInput.name = this.name;
44307         }
44308
44309         if (this.disabled) {
44310             input.disabled = true;
44311         }
44312
44313         var clg = 12 - this.inputlg;
44314         var cmd = 12 - this.inputmd;
44315         var csm = 12 - this.inputsm;
44316         var cxs = 12 - this.inputxs;
44317         
44318         var container = {
44319             tag : 'div',
44320             cls : 'row roo-money-field',
44321             cn : [
44322                 {
44323                     tag : 'div',
44324                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44325                     cn : [
44326                         {
44327                             tag : 'div',
44328                             cls: 'roo-select2-container input-group',
44329                             cn: [
44330                                 {
44331                                     tag : 'input',
44332                                     cls : 'form-control roo-money-currency-input',
44333                                     autocomplete: 'new-password',
44334                                     readOnly : 1,
44335                                     name : this.currencyName
44336                                 },
44337                                 {
44338                                     tag :'span',
44339                                     cls : 'input-group-addon',
44340                                     cn : [
44341                                         {
44342                                             tag: 'span',
44343                                             cls: 'caret'
44344                                         }
44345                                     ]
44346                                 }
44347                             ]
44348                         }
44349                     ]
44350                 },
44351                 {
44352                     tag : 'div',
44353                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44354                     cn : [
44355                         {
44356                             tag: 'div',
44357                             cls: this.hasFeedback ? 'has-feedback' : '',
44358                             cn: [
44359                                 input
44360                             ]
44361                         }
44362                     ]
44363                 }
44364             ]
44365             
44366         };
44367         
44368         if (this.fieldLabel.length) {
44369             var indicator = {
44370                 tag: 'i',
44371                 tooltip: 'This field is required'
44372             };
44373
44374             var label = {
44375                 tag: 'label',
44376                 'for':  id,
44377                 cls: 'control-label',
44378                 cn: []
44379             };
44380
44381             var label_text = {
44382                 tag: 'span',
44383                 html: this.fieldLabel
44384             };
44385
44386             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44387             label.cn = [
44388                 indicator,
44389                 label_text
44390             ];
44391
44392             if(this.indicatorpos == 'right') {
44393                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44394                 label.cn = [
44395                     label_text,
44396                     indicator
44397                 ];
44398             }
44399
44400             if(align == 'left') {
44401                 container = {
44402                     tag: 'div',
44403                     cn: [
44404                         container
44405                     ]
44406                 };
44407
44408                 if(this.labelWidth > 12){
44409                     label.style = "width: " + this.labelWidth + 'px';
44410                 }
44411                 if(this.labelWidth < 13 && this.labelmd == 0){
44412                     this.labelmd = this.labelWidth;
44413                 }
44414                 if(this.labellg > 0){
44415                     label.cls += ' col-lg-' + this.labellg;
44416                     input.cls += ' col-lg-' + (12 - this.labellg);
44417                 }
44418                 if(this.labelmd > 0){
44419                     label.cls += ' col-md-' + this.labelmd;
44420                     container.cls += ' col-md-' + (12 - this.labelmd);
44421                 }
44422                 if(this.labelsm > 0){
44423                     label.cls += ' col-sm-' + this.labelsm;
44424                     container.cls += ' col-sm-' + (12 - this.labelsm);
44425                 }
44426                 if(this.labelxs > 0){
44427                     label.cls += ' col-xs-' + this.labelxs;
44428                     container.cls += ' col-xs-' + (12 - this.labelxs);
44429                 }
44430             }
44431         }
44432
44433         cfg.cn = [
44434             label,
44435             container,
44436             hiddenInput
44437         ];
44438         
44439         var settings = this;
44440
44441         ['xs','sm','md','lg'].map(function(size){
44442             if (settings[size]) {
44443                 cfg.cls += ' col-' + size + '-' + settings[size];
44444             }
44445         });
44446         
44447         return cfg;
44448     },
44449     
44450     initEvents : function()
44451     {
44452         this.indicator = this.indicatorEl();
44453         
44454         this.initCurrencyEvent();
44455         
44456         this.initNumberEvent();
44457     },
44458     
44459     initCurrencyEvent : function()
44460     {
44461         if (!this.store) {
44462             throw "can not find store for combo";
44463         }
44464         
44465         this.store = Roo.factory(this.store, Roo.data);
44466         this.store.parent = this;
44467         
44468         this.createList();
44469         
44470         this.triggerEl = this.el.select('.input-group-addon', true).first();
44471         
44472         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44473         
44474         var _this = this;
44475         
44476         (function(){
44477             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44478             _this.list.setWidth(lw);
44479         }).defer(100);
44480         
44481         this.list.on('mouseover', this.onViewOver, this);
44482         this.list.on('mousemove', this.onViewMove, this);
44483         this.list.on('scroll', this.onViewScroll, this);
44484         
44485         if(!this.tpl){
44486             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44487         }
44488         
44489         this.view = new Roo.View(this.list, this.tpl, {
44490             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44491         });
44492         
44493         this.view.on('click', this.onViewClick, this);
44494         
44495         this.store.on('beforeload', this.onBeforeLoad, this);
44496         this.store.on('load', this.onLoad, this);
44497         this.store.on('loadexception', this.onLoadException, this);
44498         
44499         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44500             "up" : function(e){
44501                 this.inKeyMode = true;
44502                 this.selectPrev();
44503             },
44504
44505             "down" : function(e){
44506                 if(!this.isExpanded()){
44507                     this.onTriggerClick();
44508                 }else{
44509                     this.inKeyMode = true;
44510                     this.selectNext();
44511                 }
44512             },
44513
44514             "enter" : function(e){
44515                 this.collapse();
44516                 
44517                 if(this.fireEvent("specialkey", this, e)){
44518                     this.onViewClick(false);
44519                 }
44520                 
44521                 return true;
44522             },
44523
44524             "esc" : function(e){
44525                 this.collapse();
44526             },
44527
44528             "tab" : function(e){
44529                 this.collapse();
44530                 
44531                 if(this.fireEvent("specialkey", this, e)){
44532                     this.onViewClick(false);
44533                 }
44534                 
44535                 return true;
44536             },
44537
44538             scope : this,
44539
44540             doRelay : function(foo, bar, hname){
44541                 if(hname == 'down' || this.scope.isExpanded()){
44542                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44543                 }
44544                 return true;
44545             },
44546
44547             forceKeyDown: true
44548         });
44549         
44550         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44551         
44552     },
44553     
44554     initNumberEvent : function(e)
44555     {
44556         this.inputEl().on("keydown" , this.fireKey,  this);
44557         this.inputEl().on("focus", this.onFocus,  this);
44558         this.inputEl().on("blur", this.onBlur,  this);
44559         
44560         this.inputEl().relayEvent('keyup', this);
44561         
44562         if(this.indicator){
44563             this.indicator.addClass('invisible');
44564         }
44565  
44566         this.originalValue = this.getValue();
44567         
44568         if(this.validationEvent == 'keyup'){
44569             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44570             this.inputEl().on('keyup', this.filterValidation, this);
44571         }
44572         else if(this.validationEvent !== false){
44573             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44574         }
44575         
44576         if(this.selectOnFocus){
44577             this.on("focus", this.preFocus, this);
44578             
44579         }
44580         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44581             this.inputEl().on("keypress", this.filterKeys, this);
44582         } else {
44583             this.inputEl().relayEvent('keypress', this);
44584         }
44585         
44586         var allowed = "0123456789";
44587         
44588         if(this.allowDecimals){
44589             allowed += this.decimalSeparator;
44590         }
44591         
44592         if(this.allowNegative){
44593             allowed += "-";
44594         }
44595         
44596         if(this.thousandsDelimiter) {
44597             allowed += ",";
44598         }
44599         
44600         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44601         
44602         var keyPress = function(e){
44603             
44604             var k = e.getKey();
44605             
44606             var c = e.getCharCode();
44607             
44608             if(
44609                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44610                     allowed.indexOf(String.fromCharCode(c)) === -1
44611             ){
44612                 e.stopEvent();
44613                 return;
44614             }
44615             
44616             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44617                 return;
44618             }
44619             
44620             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44621                 e.stopEvent();
44622             }
44623         };
44624         
44625         this.inputEl().on("keypress", keyPress, this);
44626         
44627     },
44628     
44629     onTriggerClick : function(e)
44630     {   
44631         if(this.disabled){
44632             return;
44633         }
44634         
44635         this.page = 0;
44636         this.loadNext = false;
44637         
44638         if(this.isExpanded()){
44639             this.collapse();
44640             return;
44641         }
44642         
44643         this.hasFocus = true;
44644         
44645         if(this.triggerAction == 'all') {
44646             this.doQuery(this.allQuery, true);
44647             return;
44648         }
44649         
44650         this.doQuery(this.getRawValue());
44651     },
44652     
44653     getCurrency : function()
44654     {   
44655         var v = this.currencyEl().getValue();
44656         
44657         return v;
44658     },
44659     
44660     restrictHeight : function()
44661     {
44662         this.list.alignTo(this.currencyEl(), this.listAlign);
44663         this.list.alignTo(this.currencyEl(), this.listAlign);
44664     },
44665     
44666     onViewClick : function(view, doFocus, el, e)
44667     {
44668         var index = this.view.getSelectedIndexes()[0];
44669         
44670         var r = this.store.getAt(index);
44671         
44672         if(r){
44673             this.onSelect(r, index);
44674         }
44675     },
44676     
44677     onSelect : function(record, index){
44678         
44679         if(this.fireEvent('beforeselect', this, record, index) !== false){
44680         
44681             this.setFromCurrencyData(index > -1 ? record.data : false);
44682             
44683             this.collapse();
44684             
44685             this.fireEvent('select', this, record, index);
44686         }
44687     },
44688     
44689     setFromCurrencyData : function(o)
44690     {
44691         var currency = '';
44692         
44693         this.lastCurrency = o;
44694         
44695         if (this.currencyField) {
44696             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44697         } else {
44698             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44699         }
44700         
44701         this.lastSelectionText = currency;
44702         
44703         //setting default currency
44704         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44705             this.setCurrency(this.defaultCurrency);
44706             return;
44707         }
44708         
44709         this.setCurrency(currency);
44710     },
44711     
44712     setFromData : function(o)
44713     {
44714         var c = {};
44715         
44716         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44717         
44718         this.setFromCurrencyData(c);
44719         
44720         var value = '';
44721         
44722         if (this.name) {
44723             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44724         } else {
44725             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44726         }
44727         
44728         this.setValue(value);
44729         
44730     },
44731     
44732     setCurrency : function(v)
44733     {   
44734         this.currencyValue = v;
44735         
44736         if(this.rendered){
44737             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44738             this.validate();
44739         }
44740     },
44741     
44742     setValue : function(v)
44743     {
44744         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44745         
44746         this.value = v;
44747         
44748         if(this.rendered){
44749             
44750             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44751             
44752             this.inputEl().dom.value = (v == '') ? '' :
44753                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44754             
44755             if(!this.allowZero && v === '0') {
44756                 this.hiddenEl().dom.value = '';
44757                 this.inputEl().dom.value = '';
44758             }
44759             
44760             this.validate();
44761         }
44762     },
44763     
44764     getRawValue : function()
44765     {
44766         var v = this.inputEl().getValue();
44767         
44768         return v;
44769     },
44770     
44771     getValue : function()
44772     {
44773         return this.fixPrecision(this.parseValue(this.getRawValue()));
44774     },
44775     
44776     parseValue : function(value)
44777     {
44778         if(this.thousandsDelimiter) {
44779             value += "";
44780             r = new RegExp(",", "g");
44781             value = value.replace(r, "");
44782         }
44783         
44784         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44785         return isNaN(value) ? '' : value;
44786         
44787     },
44788     
44789     fixPrecision : function(value)
44790     {
44791         if(this.thousandsDelimiter) {
44792             value += "";
44793             r = new RegExp(",", "g");
44794             value = value.replace(r, "");
44795         }
44796         
44797         var nan = isNaN(value);
44798         
44799         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44800             return nan ? '' : value;
44801         }
44802         return parseFloat(value).toFixed(this.decimalPrecision);
44803     },
44804     
44805     decimalPrecisionFcn : function(v)
44806     {
44807         return Math.floor(v);
44808     },
44809     
44810     validateValue : function(value)
44811     {
44812         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44813             return false;
44814         }
44815         
44816         var num = this.parseValue(value);
44817         
44818         if(isNaN(num)){
44819             this.markInvalid(String.format(this.nanText, value));
44820             return false;
44821         }
44822         
44823         if(num < this.minValue){
44824             this.markInvalid(String.format(this.minText, this.minValue));
44825             return false;
44826         }
44827         
44828         if(num > this.maxValue){
44829             this.markInvalid(String.format(this.maxText, this.maxValue));
44830             return false;
44831         }
44832         
44833         return true;
44834     },
44835     
44836     validate : function()
44837     {
44838         if(this.disabled || this.allowBlank){
44839             this.markValid();
44840             return true;
44841         }
44842         
44843         var currency = this.getCurrency();
44844         
44845         if(this.validateValue(this.getRawValue()) && currency.length){
44846             this.markValid();
44847             return true;
44848         }
44849         
44850         this.markInvalid();
44851         return false;
44852     },
44853     
44854     getName: function()
44855     {
44856         return this.name;
44857     },
44858     
44859     beforeBlur : function()
44860     {
44861         if(!this.castInt){
44862             return;
44863         }
44864         
44865         var v = this.parseValue(this.getRawValue());
44866         
44867         if(v || v == 0){
44868             this.setValue(v);
44869         }
44870     },
44871     
44872     onBlur : function()
44873     {
44874         this.beforeBlur();
44875         
44876         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44877             //this.el.removeClass(this.focusClass);
44878         }
44879         
44880         this.hasFocus = false;
44881         
44882         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44883             this.validate();
44884         }
44885         
44886         var v = this.getValue();
44887         
44888         if(String(v) !== String(this.startValue)){
44889             this.fireEvent('change', this, v, this.startValue);
44890         }
44891         
44892         this.fireEvent("blur", this);
44893     },
44894     
44895     inputEl : function()
44896     {
44897         return this.el.select('.roo-money-amount-input', true).first();
44898     },
44899     
44900     currencyEl : function()
44901     {
44902         return this.el.select('.roo-money-currency-input', true).first();
44903     },
44904     
44905     hiddenEl : function()
44906     {
44907         return this.el.select('input.hidden-number-input',true).first();
44908     }
44909     
44910 });/**
44911  * @class Roo.bootstrap.BezierSignature
44912  * @extends Roo.bootstrap.Component
44913  * Bootstrap BezierSignature class
44914  * This script refer to:
44915  *    Title: Signature Pad
44916  *    Author: szimek
44917  *    Availability: https://github.com/szimek/signature_pad
44918  *
44919  * @constructor
44920  * Create a new BezierSignature
44921  * @param {Object} config The config object
44922  */
44923
44924 Roo.bootstrap.BezierSignature = function(config){
44925     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44926     this.addEvents({
44927         "resize" : true
44928     });
44929 };
44930
44931 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44932 {
44933      
44934     curve_data: [],
44935     
44936     is_empty: true,
44937     
44938     mouse_btn_down: true,
44939     
44940     /**
44941      * @cfg {int} canvas height
44942      */
44943     canvas_height: '200px',
44944     
44945     /**
44946      * @cfg {float|function} Radius of a single dot.
44947      */ 
44948     dot_size: false,
44949     
44950     /**
44951      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44952      */
44953     min_width: 0.5,
44954     
44955     /**
44956      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44957      */
44958     max_width: 2.5,
44959     
44960     /**
44961      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44962      */
44963     throttle: 16,
44964     
44965     /**
44966      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44967      */
44968     min_distance: 5,
44969     
44970     /**
44971      * @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.
44972      */
44973     bg_color: 'rgba(0, 0, 0, 0)',
44974     
44975     /**
44976      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44977      */
44978     dot_color: 'black',
44979     
44980     /**
44981      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44982      */ 
44983     velocity_filter_weight: 0.7,
44984     
44985     /**
44986      * @cfg {function} Callback when stroke begin. 
44987      */
44988     onBegin: false,
44989     
44990     /**
44991      * @cfg {function} Callback when stroke end.
44992      */
44993     onEnd: false,
44994     
44995     getAutoCreate : function()
44996     {
44997         var cls = 'roo-signature column';
44998         
44999         if(this.cls){
45000             cls += ' ' + this.cls;
45001         }
45002         
45003         var col_sizes = [
45004             'lg',
45005             'md',
45006             'sm',
45007             'xs'
45008         ];
45009         
45010         for(var i = 0; i < col_sizes.length; i++) {
45011             if(this[col_sizes[i]]) {
45012                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45013             }
45014         }
45015         
45016         var cfg = {
45017             tag: 'div',
45018             cls: cls,
45019             cn: [
45020                 {
45021                     tag: 'div',
45022                     cls: 'roo-signature-body',
45023                     cn: [
45024                         {
45025                             tag: 'canvas',
45026                             cls: 'roo-signature-body-canvas',
45027                             height: this.canvas_height,
45028                             width: this.canvas_width
45029                         }
45030                     ]
45031                 },
45032                 {
45033                     tag: 'input',
45034                     type: 'file',
45035                     style: 'display: none'
45036                 }
45037             ]
45038         };
45039         
45040         return cfg;
45041     },
45042     
45043     initEvents: function() 
45044     {
45045         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45046         
45047         var canvas = this.canvasEl();
45048         
45049         // mouse && touch event swapping...
45050         canvas.dom.style.touchAction = 'none';
45051         canvas.dom.style.msTouchAction = 'none';
45052         
45053         this.mouse_btn_down = false;
45054         canvas.on('mousedown', this._handleMouseDown, this);
45055         canvas.on('mousemove', this._handleMouseMove, this);
45056         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45057         
45058         if (window.PointerEvent) {
45059             canvas.on('pointerdown', this._handleMouseDown, this);
45060             canvas.on('pointermove', this._handleMouseMove, this);
45061             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45062         }
45063         
45064         if ('ontouchstart' in window) {
45065             canvas.on('touchstart', this._handleTouchStart, this);
45066             canvas.on('touchmove', this._handleTouchMove, this);
45067             canvas.on('touchend', this._handleTouchEnd, this);
45068         }
45069         
45070         Roo.EventManager.onWindowResize(this.resize, this, true);
45071         
45072         // file input event
45073         this.fileEl().on('change', this.uploadImage, this);
45074         
45075         this.clear();
45076         
45077         this.resize();
45078     },
45079     
45080     resize: function(){
45081         
45082         var canvas = this.canvasEl().dom;
45083         var ctx = this.canvasElCtx();
45084         var img_data = false;
45085         
45086         if(canvas.width > 0) {
45087             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45088         }
45089         // setting canvas width will clean img data
45090         canvas.width = 0;
45091         
45092         var style = window.getComputedStyle ? 
45093             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45094             
45095         var padding_left = parseInt(style.paddingLeft) || 0;
45096         var padding_right = parseInt(style.paddingRight) || 0;
45097         
45098         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45099         
45100         if(img_data) {
45101             ctx.putImageData(img_data, 0, 0);
45102         }
45103     },
45104     
45105     _handleMouseDown: function(e)
45106     {
45107         if (e.browserEvent.which === 1) {
45108             this.mouse_btn_down = true;
45109             this.strokeBegin(e);
45110         }
45111     },
45112     
45113     _handleMouseMove: function (e)
45114     {
45115         if (this.mouse_btn_down) {
45116             this.strokeMoveUpdate(e);
45117         }
45118     },
45119     
45120     _handleMouseUp: function (e)
45121     {
45122         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45123             this.mouse_btn_down = false;
45124             this.strokeEnd(e);
45125         }
45126     },
45127     
45128     _handleTouchStart: function (e) {
45129         
45130         e.preventDefault();
45131         if (e.browserEvent.targetTouches.length === 1) {
45132             // var touch = e.browserEvent.changedTouches[0];
45133             // this.strokeBegin(touch);
45134             
45135              this.strokeBegin(e); // assume e catching the correct xy...
45136         }
45137     },
45138     
45139     _handleTouchMove: function (e) {
45140         e.preventDefault();
45141         // var touch = event.targetTouches[0];
45142         // _this._strokeMoveUpdate(touch);
45143         this.strokeMoveUpdate(e);
45144     },
45145     
45146     _handleTouchEnd: function (e) {
45147         var wasCanvasTouched = e.target === this.canvasEl().dom;
45148         if (wasCanvasTouched) {
45149             e.preventDefault();
45150             // var touch = event.changedTouches[0];
45151             // _this._strokeEnd(touch);
45152             this.strokeEnd(e);
45153         }
45154     },
45155     
45156     reset: function () {
45157         this._lastPoints = [];
45158         this._lastVelocity = 0;
45159         this._lastWidth = (this.min_width + this.max_width) / 2;
45160         this.canvasElCtx().fillStyle = this.dot_color;
45161     },
45162     
45163     strokeMoveUpdate: function(e)
45164     {
45165         this.strokeUpdate(e);
45166         
45167         if (this.throttle) {
45168             this.throttleStroke(this.strokeUpdate, this.throttle);
45169         }
45170         else {
45171             this.strokeUpdate(e);
45172         }
45173     },
45174     
45175     strokeBegin: function(e)
45176     {
45177         var newPointGroup = {
45178             color: this.dot_color,
45179             points: []
45180         };
45181         
45182         if (typeof this.onBegin === 'function') {
45183             this.onBegin(e);
45184         }
45185         
45186         this.curve_data.push(newPointGroup);
45187         this.reset();
45188         this.strokeUpdate(e);
45189     },
45190     
45191     strokeUpdate: function(e)
45192     {
45193         var rect = this.canvasEl().dom.getBoundingClientRect();
45194         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45195         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45196         var lastPoints = lastPointGroup.points;
45197         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45198         var isLastPointTooClose = lastPoint
45199             ? point.distanceTo(lastPoint) <= this.min_distance
45200             : false;
45201         var color = lastPointGroup.color;
45202         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45203             var curve = this.addPoint(point);
45204             if (!lastPoint) {
45205                 this.drawDot({color: color, point: point});
45206             }
45207             else if (curve) {
45208                 this.drawCurve({color: color, curve: curve});
45209             }
45210             lastPoints.push({
45211                 time: point.time,
45212                 x: point.x,
45213                 y: point.y
45214             });
45215         }
45216     },
45217     
45218     strokeEnd: function(e)
45219     {
45220         this.strokeUpdate(e);
45221         if (typeof this.onEnd === 'function') {
45222             this.onEnd(e);
45223         }
45224     },
45225     
45226     addPoint:  function (point) {
45227         var _lastPoints = this._lastPoints;
45228         _lastPoints.push(point);
45229         if (_lastPoints.length > 2) {
45230             if (_lastPoints.length === 3) {
45231                 _lastPoints.unshift(_lastPoints[0]);
45232             }
45233             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45234             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45235             _lastPoints.shift();
45236             return curve;
45237         }
45238         return null;
45239     },
45240     
45241     calculateCurveWidths: function (startPoint, endPoint) {
45242         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45243             (1 - this.velocity_filter_weight) * this._lastVelocity;
45244
45245         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45246         var widths = {
45247             end: newWidth,
45248             start: this._lastWidth
45249         };
45250         
45251         this._lastVelocity = velocity;
45252         this._lastWidth = newWidth;
45253         return widths;
45254     },
45255     
45256     drawDot: function (_a) {
45257         var color = _a.color, point = _a.point;
45258         var ctx = this.canvasElCtx();
45259         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45260         ctx.beginPath();
45261         this.drawCurveSegment(point.x, point.y, width);
45262         ctx.closePath();
45263         ctx.fillStyle = color;
45264         ctx.fill();
45265     },
45266     
45267     drawCurve: function (_a) {
45268         var color = _a.color, curve = _a.curve;
45269         var ctx = this.canvasElCtx();
45270         var widthDelta = curve.endWidth - curve.startWidth;
45271         var drawSteps = Math.floor(curve.length()) * 2;
45272         ctx.beginPath();
45273         ctx.fillStyle = color;
45274         for (var i = 0; i < drawSteps; i += 1) {
45275         var t = i / drawSteps;
45276         var tt = t * t;
45277         var ttt = tt * t;
45278         var u = 1 - t;
45279         var uu = u * u;
45280         var uuu = uu * u;
45281         var x = uuu * curve.startPoint.x;
45282         x += 3 * uu * t * curve.control1.x;
45283         x += 3 * u * tt * curve.control2.x;
45284         x += ttt * curve.endPoint.x;
45285         var y = uuu * curve.startPoint.y;
45286         y += 3 * uu * t * curve.control1.y;
45287         y += 3 * u * tt * curve.control2.y;
45288         y += ttt * curve.endPoint.y;
45289         var width = curve.startWidth + ttt * widthDelta;
45290         this.drawCurveSegment(x, y, width);
45291         }
45292         ctx.closePath();
45293         ctx.fill();
45294     },
45295     
45296     drawCurveSegment: function (x, y, width) {
45297         var ctx = this.canvasElCtx();
45298         ctx.moveTo(x, y);
45299         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45300         this.is_empty = false;
45301     },
45302     
45303     clear: function()
45304     {
45305         var ctx = this.canvasElCtx();
45306         var canvas = this.canvasEl().dom;
45307         ctx.fillStyle = this.bg_color;
45308         ctx.clearRect(0, 0, canvas.width, canvas.height);
45309         ctx.fillRect(0, 0, canvas.width, canvas.height);
45310         this.curve_data = [];
45311         this.reset();
45312         this.is_empty = true;
45313     },
45314     
45315     fileEl: function()
45316     {
45317         return  this.el.select('input',true).first();
45318     },
45319     
45320     canvasEl: function()
45321     {
45322         return this.el.select('canvas',true).first();
45323     },
45324     
45325     canvasElCtx: function()
45326     {
45327         return this.el.select('canvas',true).first().dom.getContext('2d');
45328     },
45329     
45330     getImage: function(type)
45331     {
45332         if(this.is_empty) {
45333             return false;
45334         }
45335         
45336         // encryption ?
45337         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45338     },
45339     
45340     drawFromImage: function(img_src)
45341     {
45342         var img = new Image();
45343         
45344         img.onload = function(){
45345             this.canvasElCtx().drawImage(img, 0, 0);
45346         }.bind(this);
45347         
45348         img.src = img_src;
45349         
45350         this.is_empty = false;
45351     },
45352     
45353     selectImage: function()
45354     {
45355         this.fileEl().dom.click();
45356     },
45357     
45358     uploadImage: function(e)
45359     {
45360         var reader = new FileReader();
45361         
45362         reader.onload = function(e){
45363             var img = new Image();
45364             img.onload = function(){
45365                 this.reset();
45366                 this.canvasElCtx().drawImage(img, 0, 0);
45367             }.bind(this);
45368             img.src = e.target.result;
45369         }.bind(this);
45370         
45371         reader.readAsDataURL(e.target.files[0]);
45372     },
45373     
45374     // Bezier Point Constructor
45375     Point: (function () {
45376         function Point(x, y, time) {
45377             this.x = x;
45378             this.y = y;
45379             this.time = time || Date.now();
45380         }
45381         Point.prototype.distanceTo = function (start) {
45382             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45383         };
45384         Point.prototype.equals = function (other) {
45385             return this.x === other.x && this.y === other.y && this.time === other.time;
45386         };
45387         Point.prototype.velocityFrom = function (start) {
45388             return this.time !== start.time
45389             ? this.distanceTo(start) / (this.time - start.time)
45390             : 0;
45391         };
45392         return Point;
45393     }()),
45394     
45395     
45396     // Bezier Constructor
45397     Bezier: (function () {
45398         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45399             this.startPoint = startPoint;
45400             this.control2 = control2;
45401             this.control1 = control1;
45402             this.endPoint = endPoint;
45403             this.startWidth = startWidth;
45404             this.endWidth = endWidth;
45405         }
45406         Bezier.fromPoints = function (points, widths, scope) {
45407             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45408             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45409             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45410         };
45411         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45412             var dx1 = s1.x - s2.x;
45413             var dy1 = s1.y - s2.y;
45414             var dx2 = s2.x - s3.x;
45415             var dy2 = s2.y - s3.y;
45416             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45417             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45418             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45419             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45420             var dxm = m1.x - m2.x;
45421             var dym = m1.y - m2.y;
45422             var k = l2 / (l1 + l2);
45423             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45424             var tx = s2.x - cm.x;
45425             var ty = s2.y - cm.y;
45426             return {
45427                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45428                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45429             };
45430         };
45431         Bezier.prototype.length = function () {
45432             var steps = 10;
45433             var length = 0;
45434             var px;
45435             var py;
45436             for (var i = 0; i <= steps; i += 1) {
45437                 var t = i / steps;
45438                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45439                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45440                 if (i > 0) {
45441                     var xdiff = cx - px;
45442                     var ydiff = cy - py;
45443                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45444                 }
45445                 px = cx;
45446                 py = cy;
45447             }
45448             return length;
45449         };
45450         Bezier.prototype.point = function (t, start, c1, c2, end) {
45451             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45452             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45453             + (3.0 * c2 * (1.0 - t) * t * t)
45454             + (end * t * t * t);
45455         };
45456         return Bezier;
45457     }()),
45458     
45459     throttleStroke: function(fn, wait) {
45460       if (wait === void 0) { wait = 250; }
45461       var previous = 0;
45462       var timeout = null;
45463       var result;
45464       var storedContext;
45465       var storedArgs;
45466       var later = function () {
45467           previous = Date.now();
45468           timeout = null;
45469           result = fn.apply(storedContext, storedArgs);
45470           if (!timeout) {
45471               storedContext = null;
45472               storedArgs = [];
45473           }
45474       };
45475       return function wrapper() {
45476           var args = [];
45477           for (var _i = 0; _i < arguments.length; _i++) {
45478               args[_i] = arguments[_i];
45479           }
45480           var now = Date.now();
45481           var remaining = wait - (now - previous);
45482           storedContext = this;
45483           storedArgs = args;
45484           if (remaining <= 0 || remaining > wait) {
45485               if (timeout) {
45486                   clearTimeout(timeout);
45487                   timeout = null;
45488               }
45489               previous = now;
45490               result = fn.apply(storedContext, storedArgs);
45491               if (!timeout) {
45492                   storedContext = null;
45493                   storedArgs = [];
45494               }
45495           }
45496           else if (!timeout) {
45497               timeout = window.setTimeout(later, remaining);
45498           }
45499           return result;
45500       };
45501   }
45502   
45503 });
45504
45505  
45506
45507