sync
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * @abstract
227  * @children Roo.bootstrap.Component
228  * Bootstrap Component base class
229  * @cfg {String} cls css class
230  * @cfg {String} style any extra css
231  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
233  * @cfg {string} dataId cutomer id
234  * @cfg {string} name Specifies name attribute
235  * @cfg {string} tooltip  Text for the tooltip
236  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
237  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
238  
239  * @constructor
240  * Do not use directly - it does not do anything..
241  * @param {Object} config The config object
242  */
243
244
245
246 Roo.bootstrap.Component = function(config){
247     Roo.bootstrap.Component.superclass.constructor.call(this, config);
248        
249     this.addEvents({
250         /**
251          * @event childrenrendered
252          * Fires when the children have been rendered..
253          * @param {Roo.bootstrap.Component} this
254          */
255         "childrenrendered" : true
256         
257         
258         
259     });
260     
261     
262 };
263
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
265     
266     
267     allowDomMove : false, // to stop relocations in parent onRender...
268     
269     cls : false,
270     
271     style : false,
272     
273     autoCreate : false,
274     
275     tooltip : null,
276     /**
277      * Initialize Events for the element
278      */
279     initEvents : function() { },
280     
281     xattr : false,
282     
283     parentId : false,
284     
285     can_build_overlaid : true,
286     
287     container_method : false,
288     
289     dataId : false,
290     
291     name : false,
292     
293     parent: function() {
294         // returns the parent component..
295         return Roo.ComponentMgr.get(this.parentId)
296         
297         
298     },
299     
300     // private
301     onRender : function(ct, position)
302     {
303        // Roo.log("Call onRender: " + this.xtype);
304         
305         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
306         
307         if(this.el){
308             if (this.el.attr('xtype')) {
309                 this.el.attr('xtypex', this.el.attr('xtype'));
310                 this.el.dom.removeAttribute('xtype');
311                 
312                 this.initEvents();
313             }
314             
315             return;
316         }
317         
318          
319         
320         var cfg = Roo.apply({},  this.getAutoCreate());
321         
322         cfg.id = this.id || Roo.id();
323         
324         // fill in the extra attributes 
325         if (this.xattr && typeof(this.xattr) =='object') {
326             for (var i in this.xattr) {
327                 cfg[i] = this.xattr[i];
328             }
329         }
330         
331         if(this.dataId){
332             cfg.dataId = this.dataId;
333         }
334         
335         if (this.cls) {
336             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
337         }
338         
339         if (this.style) { // fixme needs to support more complex style data.
340             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341         }
342         
343         if(this.name){
344             cfg.name = this.name;
345         }
346         
347         this.el = ct.createChild(cfg, position);
348         
349         if (this.tooltip) {
350             this.tooltipEl().attr('tooltip', this.tooltip);
351         }
352         
353         if(this.tabIndex !== undefined){
354             this.el.dom.setAttribute('tabIndex', this.tabIndex);
355         }
356         
357         this.initEvents();
358         
359     },
360     /**
361      * Fetch the element to add children to
362      * @return {Roo.Element} defaults to this.el
363      */
364     getChildContainer : function()
365     {
366         return this.el;
367     },
368     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
369     {
370         return Roo.get(document.body);
371     },
372     
373     /**
374      * Fetch the element to display the tooltip on.
375      * @return {Roo.Element} defaults to this.el
376      */
377     tooltipEl : function()
378     {
379         return this.el;
380     },
381         
382     addxtype  : function(tree,cntr)
383     {
384         var cn = this;
385         
386         cn = Roo.factory(tree);
387         //Roo.log(['addxtype', cn]);
388            
389         cn.parentType = this.xtype; //??
390         cn.parentId = this.id;
391         
392         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393         if (typeof(cn.container_method) == 'string') {
394             cntr = cn.container_method;
395         }
396         
397         
398         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
399         
400         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
401         
402         var build_from_html =  Roo.XComponent.build_from_html;
403           
404         var is_body  = (tree.xtype == 'Body') ;
405           
406         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
407           
408         var self_cntr_el = Roo.get(this[cntr](false));
409         
410         // do not try and build conditional elements 
411         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412             return false;
413         }
414         
415         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417                 return this.addxtypeChild(tree,cntr, is_body);
418             }
419             
420             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
421                 
422             if(echild){
423                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
424             }
425             
426             Roo.log('skipping render');
427             return cn;
428             
429         }
430         
431         var ret = false;
432         if (!build_from_html) {
433             return false;
434         }
435         
436         // this i think handles overlaying multiple children of the same type
437         // with the sam eelement.. - which might be buggy..
438         while (true) {
439             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
440             
441             if (!echild) {
442                 break;
443             }
444             
445             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446                 break;
447             }
448             
449             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
450         }
451        
452         return ret;
453     },
454     
455     
456     addxtypeChild : function (tree, cntr, is_body)
457     {
458         Roo.debug && Roo.log('addxtypeChild:' + cntr);
459         var cn = this;
460         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
461         
462         
463         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464                     (typeof(tree['flexy:foreach']) != 'undefined');
465           
466     
467         
468         skip_children = false;
469         // render the element if it's not BODY.
470         if (!is_body) {
471             
472             // if parent was disabled, then do not try and create the children..
473             if(!this[cntr](true)){
474                 tree.items = [];
475                 return tree;
476             }
477            
478             cn = Roo.factory(tree);
479            
480             cn.parentType = this.xtype; //??
481             cn.parentId = this.id;
482             
483             var build_from_html =  Roo.XComponent.build_from_html;
484             
485             
486             // does the container contain child eleemnts with 'xtype' attributes.
487             // that match this xtype..
488             // note - when we render we create these as well..
489             // so we should check to see if body has xtype set.
490             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
491                
492                 var self_cntr_el = Roo.get(this[cntr](false));
493                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
494                 if (echild) { 
495                     //Roo.log(Roo.XComponent.build_from_html);
496                     //Roo.log("got echild:");
497                     //Roo.log(echild);
498                 }
499                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500                 // and are not displayed -this causes this to use up the wrong element when matching.
501                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
502                 
503                 
504                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
506                   
507                   
508                   
509                     cn.el = echild;
510                   //  Roo.log("GOT");
511                     //echild.dom.removeAttribute('xtype');
512                 } else {
513                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514                     Roo.debug && Roo.log(self_cntr_el);
515                     Roo.debug && Roo.log(echild);
516                     Roo.debug && Roo.log(cn);
517                 }
518             }
519            
520             
521            
522             // if object has flexy:if - then it may or may not be rendered.
523             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
524                 // skip a flexy if element.
525                 Roo.debug && Roo.log('skipping render');
526                 Roo.debug && Roo.log(tree);
527                 if (!cn.el) {
528                     Roo.debug && Roo.log('skipping all children');
529                     skip_children = true;
530                 }
531                 
532              } else {
533                  
534                 // actually if flexy:foreach is found, we really want to create 
535                 // multiple copies here...
536                 //Roo.log('render');
537                 //Roo.log(this[cntr]());
538                 // some elements do not have render methods.. like the layouts...
539                 /*
540                 if(this[cntr](true) === false){
541                     cn.items = [];
542                     return cn;
543                 }
544                 */
545                 cn.render && cn.render(this[cntr](true));
546                 
547              }
548             // then add the element..
549         }
550          
551         // handle the kids..
552         
553         var nitems = [];
554         /*
555         if (typeof (tree.menu) != 'undefined') {
556             tree.menu.parentType = cn.xtype;
557             tree.menu.triggerEl = cn.el;
558             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559             
560         }
561         */
562         if (!tree.items || !tree.items.length) {
563             cn.items = nitems;
564             //Roo.log(["no children", this]);
565             
566             return cn;
567         }
568          
569         var items = tree.items;
570         delete tree.items;
571         
572         //Roo.log(items.length);
573             // add the items..
574         if (!skip_children) {    
575             for(var i =0;i < items.length;i++) {
576               //  Roo.log(['add child', items[i]]);
577                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
578             }
579         }
580         
581         cn.items = nitems;
582         
583         //Roo.log("fire childrenrendered");
584         
585         cn.fireEvent('childrenrendered', this);
586         
587         return cn;
588     },
589     
590     /**
591      * Set the element that will be used to show or hide
592      */
593     setVisibilityEl : function(el)
594     {
595         this.visibilityEl = el;
596     },
597     
598      /**
599      * Get the element that will be used to show or hide
600      */
601     getVisibilityEl : function()
602     {
603         if (typeof(this.visibilityEl) == 'object') {
604             return this.visibilityEl;
605         }
606         
607         if (typeof(this.visibilityEl) == 'string') {
608             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
609         }
610         
611         return this.getEl();
612     },
613     
614     /**
615      * Show a component - removes 'hidden' class
616      */
617     show : function()
618     {
619         if(!this.getVisibilityEl()){
620             return;
621         }
622          
623         this.getVisibilityEl().removeClass(['hidden','d-none']);
624         
625         this.fireEvent('show', this);
626         
627         
628     },
629     /**
630      * Hide a component - adds 'hidden' class
631      */
632     hide: function()
633     {
634         if(!this.getVisibilityEl()){
635             return;
636         }
637         
638         this.getVisibilityEl().addClass(['hidden','d-none']);
639         
640         this.fireEvent('hide', this);
641         
642     }
643 });
644
645  /*
646  * - LGPL
647  *
648  * element
649  * 
650  */
651
652 /**
653  * @class Roo.bootstrap.Element
654  * @extends Roo.bootstrap.Component
655  * @children Roo.bootstrap.Component
656  * Bootstrap Element class (basically a DIV used to make random stuff )
657  * 
658  * @cfg {String} html contents of the element
659  * @cfg {String} tag tag of the element
660  * @cfg {String} cls class of the element
661  * @cfg {Boolean} preventDefault (true|false) default false
662  * @cfg {Boolean} clickable (true|false) default false
663  * @cfg {String} role default blank - set to button to force cursor pointer
664  
665  * 
666  * @constructor
667  * Create a new Element
668  * @param {Object} config The config object
669  */
670
671 Roo.bootstrap.Element = function(config){
672     Roo.bootstrap.Element.superclass.constructor.call(this, config);
673     
674     this.addEvents({
675         // raw events
676         /**
677          * @event click
678          * When a element is chick
679          * @param {Roo.bootstrap.Element} this
680          * @param {Roo.EventObject} e
681          */
682         "click" : true 
683         
684       
685     });
686 };
687
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
689     
690     tag: 'div',
691     cls: '',
692     html: '',
693     preventDefault: false, 
694     clickable: false,
695     tapedTwice : false,
696     role : false,
697     
698     getAutoCreate : function(){
699         
700         var cfg = {
701             tag: this.tag,
702             // cls: this.cls, double assign in parent class Component.js :: onRender
703             html: this.html
704         };
705         if (this.role !== false) {
706             cfg.role = this.role;
707         }
708         
709         return cfg;
710     },
711     
712     initEvents: function() 
713     {
714         Roo.bootstrap.Element.superclass.initEvents.call(this);
715         
716         if(this.clickable){
717             this.el.on('click', this.onClick, this);
718         }
719         
720         
721     },
722     
723     onClick : function(e)
724     {
725         if(this.preventDefault){
726             e.preventDefault();
727         }
728         
729         this.fireEvent('click', this, e); // why was this double click before?
730     },
731     
732     
733     
734
735     
736     
737     getValue : function()
738     {
739         return this.el.dom.innerHTML;
740     },
741     
742     setValue : function(value)
743     {
744         this.el.dom.innerHTML = value;
745     }
746    
747 });
748
749  
750
751  /*
752  * - LGPL
753  *
754  * dropable area
755  * 
756  */
757
758 /**
759  * @class Roo.bootstrap.DropTarget
760  * @extends Roo.bootstrap.Element
761  * Bootstrap DropTarget class
762  
763  * @cfg {string} name dropable name
764  * 
765  * @constructor
766  * Create a new Dropable Area
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.DropTarget = function(config){
771     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772     
773     this.addEvents({
774         // raw events
775         /**
776          * @event click
777          * When a element is chick
778          * @param {Roo.bootstrap.Element} this
779          * @param {Roo.EventObject} e
780          */
781         "drop" : true
782     });
783 };
784
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
786     
787     
788     getAutoCreate : function(){
789         
790          
791     },
792     
793     initEvents: function() 
794     {
795         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
797             ddGroup: this.name,
798             listeners : {
799                 drop : this.dragDrop.createDelegate(this),
800                 enter : this.dragEnter.createDelegate(this),
801                 out : this.dragOut.createDelegate(this),
802                 over : this.dragOver.createDelegate(this)
803             }
804             
805         });
806         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
807     },
808     
809     dragDrop : function(source,e,data)
810     {
811         // user has to decide how to impliment this.
812         Roo.log('drop');
813         Roo.log(this);
814         //this.fireEvent('drop', this, source, e ,data);
815         return false;
816     },
817     
818     dragEnter : function(n, dd, e, data)
819     {
820         // probably want to resize the element to match the dropped element..
821         Roo.log("enter");
822         this.originalSize = this.el.getSize();
823         this.el.setSize( n.el.getSize());
824         this.dropZone.DDM.refreshCache(this.name);
825         Roo.log([n, dd, e, data]);
826     },
827     
828     dragOut : function(value)
829     {
830         // resize back to normal
831         Roo.log("out");
832         this.el.setSize(this.originalSize);
833         this.dropZone.resetConstraints();
834     },
835     
836     dragOver : function()
837     {
838         // ??? do nothing?
839     }
840    
841 });
842
843  
844
845  /*
846  * - LGPL
847  *
848  * Body
849  *
850  */
851
852 /**
853  * @class Roo.bootstrap.Body
854  * @extends Roo.bootstrap.Component
855  * @builder-top
856  * @children Roo.bootstrap.Component
857  * @parent none
858  * Bootstrap Body class
859  *
860  * @constructor
861  * Create a new body
862  * @param {Object} config The config object
863  */
864
865 Roo.bootstrap.Body = function(config){
866
867     config = config || {};
868
869     Roo.bootstrap.Body.superclass.constructor.call(this, config);
870     this.el = Roo.get(config.el ? config.el : document.body );
871     if (this.cls && this.cls.length) {
872         Roo.get(document.body).addClass(this.cls);
873     }
874 };
875
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
877
878     is_body : true,// just to make sure it's constructed?
879
880         autoCreate : {
881         cls: 'container'
882     },
883     onRender : function(ct, position)
884     {
885        /* Roo.log("Roo.bootstrap.Body - onRender");
886         if (this.cls && this.cls.length) {
887             Roo.get(document.body).addClass(this.cls);
888         }
889         // style??? xttr???
890         */
891     }
892
893
894
895
896 });
897 /*
898  * - LGPL
899  *
900  * button group
901  * 
902  */
903
904
905 /**
906  * @class Roo.bootstrap.ButtonGroup
907  * @extends Roo.bootstrap.Component
908  * Bootstrap ButtonGroup class
909  * @children Roo.bootstrap.Button Roo.bootstrap.Form
910  * 
911  * @cfg {String} size lg | sm | xs (default empty normal)
912  * @cfg {String} align vertical | justified  (default none)
913  * @cfg {String} direction up | down (default down)
914  * @cfg {Boolean} toolbar false | true
915  * @cfg {Boolean} btn true | false
916  * 
917  * 
918  * @constructor
919  * Create a new Input
920  * @param {Object} config The config object
921  */
922
923 Roo.bootstrap.ButtonGroup = function(config){
924     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
925 };
926
927 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
928     
929     size: '',
930     align: '',
931     direction: '',
932     toolbar: false,
933     btn: true,
934
935     getAutoCreate : function(){
936         var cfg = {
937             cls: 'btn-group',
938             html : null
939         };
940         
941         cfg.html = this.html || cfg.html;
942         
943         if (this.toolbar) {
944             cfg = {
945                 cls: 'btn-toolbar',
946                 html: null
947             };
948             
949             return cfg;
950         }
951         
952         if (['vertical','justified'].indexOf(this.align)!==-1) {
953             cfg.cls = 'btn-group-' + this.align;
954             
955             if (this.align == 'justified') {
956                 console.log(this.items);
957             }
958         }
959         
960         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
961             cfg.cls += ' btn-group-' + this.size;
962         }
963         
964         if (this.direction == 'up') {
965             cfg.cls += ' dropup' ;
966         }
967         
968         return cfg;
969     },
970     /**
971      * Add a button to the group (similar to NavItem API.)
972      */
973     addItem : function(cfg)
974     {
975         var cn = new Roo.bootstrap.Button(cfg);
976         //this.register(cn);
977         cn.parentId = this.id;
978         cn.onRender(this.el, null);
979         return cn;
980     }
981    
982 });
983
984  /*
985  * - LGPL
986  *
987  * button
988  * 
989  */
990
991 /**
992  * @class Roo.bootstrap.Button
993  * @extends Roo.bootstrap.Component
994  * Bootstrap Button class
995  * @cfg {String} html The button content
996  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
997  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
998  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
999  * @cfg {String} size (lg|sm|xs)
1000  * @cfg {String} tag (a|input|submit)
1001  * @cfg {String} href empty or href
1002  * @cfg {Boolean} disabled default false;
1003  * @cfg {Boolean} isClose default false;
1004  * @cfg {String} glyphicon depricated - use fa
1005  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1006  * @cfg {String} badge text for badge
1007  * @cfg {String} theme (default|glow)  
1008  * @cfg {Boolean} inverse dark themed version
1009  * @cfg {Boolean} toggle is it a slidy toggle button
1010  * @cfg {Boolean} pressed   default null - if the button ahs active state
1011  * @cfg {String} ontext text for on slidy toggle state
1012  * @cfg {String} offtext text for off slidy toggle state
1013  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1014  * @cfg {Boolean} removeClass remove the standard class..
1015  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1016  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1017  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1018
1019  * @constructor
1020  * Create a new button
1021  * @param {Object} config The config object
1022  */
1023
1024
1025 Roo.bootstrap.Button = function(config){
1026     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1027     
1028     this.addEvents({
1029         // raw events
1030         /**
1031          * @event click
1032          * When a button is pressed
1033          * @param {Roo.bootstrap.Button} btn
1034          * @param {Roo.EventObject} e
1035          */
1036         "click" : true,
1037         /**
1038          * @event dblclick
1039          * When a button is double clicked
1040          * @param {Roo.bootstrap.Button} btn
1041          * @param {Roo.EventObject} e
1042          */
1043         "dblclick" : true,
1044          /**
1045          * @event toggle
1046          * After the button has been toggles
1047          * @param {Roo.bootstrap.Button} btn
1048          * @param {Roo.EventObject} e
1049          * @param {boolean} pressed (also available as button.pressed)
1050          */
1051         "toggle" : true
1052     });
1053 };
1054
1055 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1056     html: false,
1057     active: false,
1058     weight: '',
1059     badge_weight: '',
1060     outline : false,
1061     size: '',
1062     tag: 'button',
1063     href: '',
1064     disabled: false,
1065     isClose: false,
1066     glyphicon: '',
1067     fa: '',
1068     badge: '',
1069     theme: 'default',
1070     inverse: false,
1071     
1072     toggle: false,
1073     ontext: 'ON',
1074     offtext: 'OFF',
1075     defaulton: true,
1076     preventDefault: true,
1077     removeClass: false,
1078     name: false,
1079     target: false,
1080     group : false,
1081      
1082     pressed : null,
1083      
1084     
1085     getAutoCreate : function(){
1086         
1087         var cfg = {
1088             tag : 'button',
1089             cls : 'roo-button',
1090             html: ''
1091         };
1092         
1093         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1094             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1095             this.tag = 'button';
1096         } else {
1097             cfg.tag = this.tag;
1098         }
1099         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1100         
1101         if (this.toggle == true) {
1102             cfg={
1103                 tag: 'div',
1104                 cls: 'slider-frame roo-button',
1105                 cn: [
1106                     {
1107                         tag: 'span',
1108                         'data-on-text':'ON',
1109                         'data-off-text':'OFF',
1110                         cls: 'slider-button',
1111                         html: this.offtext
1112                     }
1113                 ]
1114             };
1115             // why are we validating the weights?
1116             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1117                 cfg.cls +=  ' ' + this.weight;
1118             }
1119             
1120             return cfg;
1121         }
1122         
1123         if (this.isClose) {
1124             cfg.cls += ' close';
1125             
1126             cfg["aria-hidden"] = true;
1127             
1128             cfg.html = "&times;";
1129             
1130             return cfg;
1131         }
1132              
1133         
1134         if (this.theme==='default') {
1135             cfg.cls = 'btn roo-button';
1136             
1137             //if (this.parentType != 'Navbar') {
1138             this.weight = this.weight.length ?  this.weight : 'default';
1139             //}
1140             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1141                 
1142                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1143                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1144                 cfg.cls += ' btn-' + outline + weight;
1145                 if (this.weight == 'default') {
1146                     // BC
1147                     cfg.cls += ' btn-' + this.weight;
1148                 }
1149             }
1150         } else if (this.theme==='glow') {
1151             
1152             cfg.tag = 'a';
1153             cfg.cls = 'btn-glow roo-button';
1154             
1155             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1156                 
1157                 cfg.cls += ' ' + this.weight;
1158             }
1159         }
1160    
1161         
1162         if (this.inverse) {
1163             this.cls += ' inverse';
1164         }
1165         
1166         
1167         if (this.active || this.pressed === true) {
1168             cfg.cls += ' active';
1169         }
1170         
1171         if (this.disabled) {
1172             cfg.disabled = 'disabled';
1173         }
1174         
1175         if (this.items) {
1176             Roo.log('changing to ul' );
1177             cfg.tag = 'ul';
1178             this.glyphicon = 'caret';
1179             if (Roo.bootstrap.version == 4) {
1180                 this.fa = 'caret-down';
1181             }
1182             
1183         }
1184         
1185         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1186          
1187         //gsRoo.log(this.parentType);
1188         if (this.parentType === 'Navbar' && !this.parent().bar) {
1189             Roo.log('changing to li?');
1190             
1191             cfg.tag = 'li';
1192             
1193             cfg.cls = '';
1194             cfg.cn =  [{
1195                 tag : 'a',
1196                 cls : 'roo-button',
1197                 html : this.html,
1198                 href : this.href || '#'
1199             }];
1200             if (this.menu) {
1201                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1202                 cfg.cls += ' dropdown';
1203             }   
1204             
1205             delete cfg.html;
1206             
1207         }
1208         
1209        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1210         
1211         if (this.glyphicon) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'span',
1217                     cls: 'glyphicon glyphicon-' + this.glyphicon
1218                 }
1219             ];
1220         }
1221         if (this.fa) {
1222             cfg.html = ' ' + cfg.html;
1223             
1224             cfg.cn = [
1225                 {
1226                     tag: 'i',
1227                     cls: 'fa fas fa-' + this.fa
1228                 }
1229             ];
1230         }
1231         
1232         if (this.badge) {
1233             cfg.html += ' ';
1234             
1235             cfg.tag = 'a';
1236             
1237 //            cfg.cls='btn roo-button';
1238             
1239             cfg.href=this.href;
1240             
1241             var value = cfg.html;
1242             
1243             if(this.glyphicon){
1244                 value = {
1245                     tag: 'span',
1246                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1247                     html: this.html
1248                 };
1249             }
1250             if(this.fa){
1251                 value = {
1252                     tag: 'i',
1253                     cls: 'fa fas fa-' + this.fa,
1254                     html: this.html
1255                 };
1256             }
1257             
1258             var bw = this.badge_weight.length ? this.badge_weight :
1259                 (this.weight.length ? this.weight : 'secondary');
1260             bw = bw == 'default' ? 'secondary' : bw;
1261             
1262             cfg.cn = [
1263                 value,
1264                 {
1265                     tag: 'span',
1266                     cls: 'badge badge-' + bw,
1267                     html: this.badge
1268                 }
1269             ];
1270             
1271             cfg.html='';
1272         }
1273         
1274         if (this.menu) {
1275             cfg.cls += ' dropdown';
1276             cfg.html = typeof(cfg.html) != 'undefined' ?
1277                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1278         }
1279         
1280         if (cfg.tag !== 'a' && this.href !== '') {
1281             throw "Tag must be a to set href.";
1282         } else if (this.href.length > 0) {
1283             cfg.href = this.href;
1284         }
1285         
1286         if(this.removeClass){
1287             cfg.cls = '';
1288         }
1289         
1290         if(this.target){
1291             cfg.target = this.target;
1292         }
1293         
1294         return cfg;
1295     },
1296     initEvents: function() {
1297        // Roo.log('init events?');
1298 //        Roo.log(this.el.dom);
1299         // add the menu...
1300         
1301         if (typeof (this.menu) != 'undefined') {
1302             this.menu.parentType = this.xtype;
1303             this.menu.triggerEl = this.el;
1304             this.addxtype(Roo.apply({}, this.menu));
1305         }
1306
1307
1308         if (this.el.hasClass('roo-button')) {
1309              this.el.on('click', this.onClick, this);
1310              this.el.on('dblclick', this.onDblClick, this);
1311         } else {
1312              this.el.select('.roo-button').on('click', this.onClick, this);
1313              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1314              
1315         }
1316         // why?
1317         if(this.removeClass){
1318             this.el.on('click', this.onClick, this);
1319         }
1320         
1321         if (this.group === true) {
1322              if (this.pressed === false || this.pressed === true) {
1323                 // nothing
1324             } else {
1325                 this.pressed = false;
1326                 this.setActive(this.pressed);
1327             }
1328             
1329         }
1330         
1331         this.el.enableDisplayMode();
1332         
1333     },
1334     onClick : function(e)
1335     {
1336         if (this.disabled) {
1337             return;
1338         }
1339         
1340         Roo.log('button on click ');
1341         if(this.preventDefault){
1342             e.preventDefault();
1343         }
1344         
1345         if (this.group) {
1346             if (this.pressed) {
1347                 // do nothing -
1348                 return;
1349             }
1350             this.setActive(true);
1351             var pi = this.parent().items;
1352             for (var i = 0;i < pi.length;i++) {
1353                 if (this == pi[i]) {
1354                     continue;
1355                 }
1356                 if (pi[i].el.hasClass('roo-button')) {
1357                     pi[i].setActive(false);
1358                 }
1359             }
1360             this.fireEvent('click', this, e);            
1361             return;
1362         }
1363         
1364         if (this.pressed === true || this.pressed === false) {
1365             this.toggleActive(e);
1366         }
1367         
1368         
1369         this.fireEvent('click', this, e);
1370     },
1371     onDblClick: function(e)
1372     {
1373         if (this.disabled) {
1374             return;
1375         }
1376         if(this.preventDefault){
1377             e.preventDefault();
1378         }
1379         this.fireEvent('dblclick', this, e);
1380     },
1381     /**
1382      * Enables this button
1383      */
1384     enable : function()
1385     {
1386         this.disabled = false;
1387         this.el.removeClass('disabled');
1388         this.el.dom.removeAttribute("disabled");
1389     },
1390     
1391     /**
1392      * Disable this button
1393      */
1394     disable : function()
1395     {
1396         this.disabled = true;
1397         this.el.addClass('disabled');
1398         this.el.attr("disabled", "disabled")
1399     },
1400      /**
1401      * sets the active state on/off, 
1402      * @param {Boolean} state (optional) Force a particular state
1403      */
1404     setActive : function(v) {
1405         
1406         this.el[v ? 'addClass' : 'removeClass']('active');
1407         this.pressed = v;
1408     },
1409      /**
1410      * toggles the current active state 
1411      */
1412     toggleActive : function(e)
1413     {
1414         this.setActive(!this.pressed); // this modifies pressed...
1415         this.fireEvent('toggle', this, e, this.pressed);
1416     },
1417      /**
1418      * get the current active state
1419      * @return {boolean} true if it's active
1420      */
1421     isActive : function()
1422     {
1423         return this.el.hasClass('active');
1424     },
1425     /**
1426      * set the text of the first selected button
1427      */
1428     setText : function(str)
1429     {
1430         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1431     },
1432     /**
1433      * get the text of the first selected button
1434      */
1435     getText : function()
1436     {
1437         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1438     },
1439     
1440     setWeight : function(str)
1441     {
1442         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1443         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1444         this.weight = str;
1445         var outline = this.outline ? 'outline-' : '';
1446         if (str == 'default') {
1447             this.el.addClass('btn-default btn-outline-secondary');        
1448             return;
1449         }
1450         this.el.addClass('btn-' + outline + str);        
1451     }
1452     
1453     
1454 });
1455 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1456
1457 Roo.bootstrap.Button.weights = [
1458     'default',
1459     'secondary' ,
1460     'primary',
1461     'success',
1462     'info',
1463     'warning',
1464     'danger',
1465     'link',
1466     'light',
1467     'dark'              
1468    
1469 ];/*
1470  * - LGPL
1471  *
1472  * column
1473  * 
1474  */
1475
1476 /**
1477  * @class Roo.bootstrap.Column
1478  * @extends Roo.bootstrap.Component
1479  * @children Roo.bootstrap.Component
1480  * Bootstrap Column class
1481  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1482  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1483  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1484  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1485  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1486  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1487  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1488  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1489  *
1490  * 
1491  * @cfg {Boolean} hidden (true|false) hide the element
1492  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1493  * @cfg {String} fa (ban|check|...) font awesome icon
1494  * @cfg {Number} fasize (1|2|....) font awsome size
1495
1496  * @cfg {String} icon (info-sign|check|...) glyphicon name
1497
1498  * @cfg {String} html content of column.
1499  * 
1500  * @constructor
1501  * Create a new Column
1502  * @param {Object} config The config object
1503  */
1504
1505 Roo.bootstrap.Column = function(config){
1506     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1507 };
1508
1509 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1510     
1511     xs: false,
1512     sm: false,
1513     md: false,
1514     lg: false,
1515     xsoff: false,
1516     smoff: false,
1517     mdoff: false,
1518     lgoff: false,
1519     html: '',
1520     offset: 0,
1521     alert: false,
1522     fa: false,
1523     icon : false,
1524     hidden : false,
1525     fasize : 1,
1526     
1527     getAutoCreate : function(){
1528         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1529         
1530         cfg = {
1531             tag: 'div',
1532             cls: 'column'
1533         };
1534         
1535         var settings=this;
1536         var sizes =   ['xs','sm','md','lg'];
1537         sizes.map(function(size ,ix){
1538             //Roo.log( size + ':' + settings[size]);
1539             
1540             if (settings[size+'off'] !== false) {
1541                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1542             }
1543             
1544             if (settings[size] === false) {
1545                 return;
1546             }
1547             
1548             if (!settings[size]) { // 0 = hidden
1549                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1550                 // bootsrap4
1551                 for (var i = ix; i > -1; i--) {
1552                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1553                 }
1554                 
1555                 
1556                 return;
1557             }
1558             cfg.cls += ' col-' + size + '-' + settings[size] + (
1559                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1560             );
1561             
1562         });
1563         
1564         if (this.hidden) {
1565             cfg.cls += ' hidden';
1566         }
1567         
1568         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1569             cfg.cls +=' alert alert-' + this.alert;
1570         }
1571         
1572         
1573         if (this.html.length) {
1574             cfg.html = this.html;
1575         }
1576         if (this.fa) {
1577             var fasize = '';
1578             if (this.fasize > 1) {
1579                 fasize = ' fa-' + this.fasize + 'x';
1580             }
1581             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1582             
1583             
1584         }
1585         if (this.icon) {
1586             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1587         }
1588         
1589         return cfg;
1590     }
1591    
1592 });
1593
1594  
1595
1596  /*
1597  * - LGPL
1598  *
1599  * page container.
1600  * 
1601  */
1602
1603
1604 /**
1605  * @class Roo.bootstrap.Container
1606  * @extends Roo.bootstrap.Component
1607  * @builder-top
1608  * @children Roo.bootstrap.Component
1609  * Bootstrap Container class
1610  * @cfg {Boolean} jumbotron is it a jumbotron element
1611  * @cfg {String} html content of element
1612  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1613  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1614  * @cfg {String} header content of header (for panel)
1615  * @cfg {String} footer content of footer (for panel)
1616  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1617  * @cfg {String} tag (header|aside|section) type of HTML tag.
1618  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1619  * @cfg {String} fa font awesome icon
1620  * @cfg {String} icon (info-sign|check|...) glyphicon name
1621  * @cfg {Boolean} hidden (true|false) hide the element
1622  * @cfg {Boolean} expandable (true|false) default false
1623  * @cfg {Boolean} expanded (true|false) default true
1624  * @cfg {String} rheader contet on the right of header
1625  * @cfg {Boolean} clickable (true|false) default false
1626
1627  *     
1628  * @constructor
1629  * Create a new Container
1630  * @param {Object} config The config object
1631  */
1632
1633 Roo.bootstrap.Container = function(config){
1634     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1635     
1636     this.addEvents({
1637         // raw events
1638          /**
1639          * @event expand
1640          * After the panel has been expand
1641          * 
1642          * @param {Roo.bootstrap.Container} this
1643          */
1644         "expand" : true,
1645         /**
1646          * @event collapse
1647          * After the panel has been collapsed
1648          * 
1649          * @param {Roo.bootstrap.Container} this
1650          */
1651         "collapse" : true,
1652         /**
1653          * @event click
1654          * When a element is chick
1655          * @param {Roo.bootstrap.Container} this
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1663     
1664     jumbotron : false,
1665     well: '',
1666     panel : '',
1667     header: '',
1668     footer : '',
1669     sticky: '',
1670     tag : false,
1671     alert : false,
1672     fa: false,
1673     icon : false,
1674     expandable : false,
1675     rheader : '',
1676     expanded : true,
1677     clickable: false,
1678   
1679      
1680     getChildContainer : function() {
1681         
1682         if(!this.el){
1683             return false;
1684         }
1685         
1686         if (this.panel.length) {
1687             return this.el.select('.panel-body',true).first();
1688         }
1689         
1690         return this.el;
1691     },
1692     
1693     
1694     getAutoCreate : function(){
1695         
1696         var cfg = {
1697             tag : this.tag || 'div',
1698             html : '',
1699             cls : ''
1700         };
1701         if (this.jumbotron) {
1702             cfg.cls = 'jumbotron';
1703         }
1704         
1705         
1706         
1707         // - this is applied by the parent..
1708         //if (this.cls) {
1709         //    cfg.cls = this.cls + '';
1710         //}
1711         
1712         if (this.sticky.length) {
1713             
1714             var bd = Roo.get(document.body);
1715             if (!bd.hasClass('bootstrap-sticky')) {
1716                 bd.addClass('bootstrap-sticky');
1717                 Roo.select('html',true).setStyle('height', '100%');
1718             }
1719              
1720             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1721         }
1722         
1723         
1724         if (this.well.length) {
1725             switch (this.well) {
1726                 case 'lg':
1727                 case 'sm':
1728                     cfg.cls +=' well well-' +this.well;
1729                     break;
1730                 default:
1731                     cfg.cls +=' well';
1732                     break;
1733             }
1734         }
1735         
1736         if (this.hidden) {
1737             cfg.cls += ' hidden';
1738         }
1739         
1740         
1741         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1742             cfg.cls +=' alert alert-' + this.alert;
1743         }
1744         
1745         var body = cfg;
1746         
1747         if (this.panel.length) {
1748             cfg.cls += ' panel panel-' + this.panel;
1749             cfg.cn = [];
1750             if (this.header.length) {
1751                 
1752                 var h = [];
1753                 
1754                 if(this.expandable){
1755                     
1756                     cfg.cls = cfg.cls + ' expandable';
1757                     
1758                     h.push({
1759                         tag: 'i',
1760                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1761                     });
1762                     
1763                 }
1764                 
1765                 h.push(
1766                     {
1767                         tag: 'span',
1768                         cls : 'panel-title',
1769                         html : (this.expandable ? '&nbsp;' : '') + this.header
1770                     },
1771                     {
1772                         tag: 'span',
1773                         cls: 'panel-header-right',
1774                         html: this.rheader
1775                     }
1776                 );
1777                 
1778                 cfg.cn.push({
1779                     cls : 'panel-heading',
1780                     style : this.expandable ? 'cursor: pointer' : '',
1781                     cn : h
1782                 });
1783                 
1784             }
1785             
1786             body = false;
1787             cfg.cn.push({
1788                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1789                 html : this.html
1790             });
1791             
1792             
1793             if (this.footer.length) {
1794                 cfg.cn.push({
1795                     cls : 'panel-footer',
1796                     html : this.footer
1797                     
1798                 });
1799             }
1800             
1801         }
1802         
1803         if (body) {
1804             body.html = this.html || cfg.html;
1805             // prefix with the icons..
1806             if (this.fa) {
1807                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1808             }
1809             if (this.icon) {
1810                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1811             }
1812             
1813             
1814         }
1815         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1816             cfg.cls =  'container';
1817         }
1818         
1819         return cfg;
1820     },
1821     
1822     initEvents: function() 
1823     {
1824         if(this.expandable){
1825             var headerEl = this.headerEl();
1826         
1827             if(headerEl){
1828                 headerEl.on('click', this.onToggleClick, this);
1829             }
1830         }
1831         
1832         if(this.clickable){
1833             this.el.on('click', this.onClick, this);
1834         }
1835         
1836     },
1837     
1838     onToggleClick : function()
1839     {
1840         var headerEl = this.headerEl();
1841         
1842         if(!headerEl){
1843             return;
1844         }
1845         
1846         if(this.expanded){
1847             this.collapse();
1848             return;
1849         }
1850         
1851         this.expand();
1852     },
1853     
1854     expand : function()
1855     {
1856         if(this.fireEvent('expand', this)) {
1857             
1858             this.expanded = true;
1859             
1860             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1861             
1862             this.el.select('.panel-body',true).first().removeClass('hide');
1863             
1864             var toggleEl = this.toggleEl();
1865
1866             if(!toggleEl){
1867                 return;
1868             }
1869
1870             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1871         }
1872         
1873     },
1874     
1875     collapse : function()
1876     {
1877         if(this.fireEvent('collapse', this)) {
1878             
1879             this.expanded = false;
1880             
1881             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1882             this.el.select('.panel-body',true).first().addClass('hide');
1883         
1884             var toggleEl = this.toggleEl();
1885
1886             if(!toggleEl){
1887                 return;
1888             }
1889
1890             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1891         }
1892     },
1893     
1894     toggleEl : function()
1895     {
1896         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1897             return;
1898         }
1899         
1900         return this.el.select('.panel-heading .fa',true).first();
1901     },
1902     
1903     headerEl : function()
1904     {
1905         if(!this.el || !this.panel.length || !this.header.length){
1906             return;
1907         }
1908         
1909         return this.el.select('.panel-heading',true).first()
1910     },
1911     
1912     bodyEl : function()
1913     {
1914         if(!this.el || !this.panel.length){
1915             return;
1916         }
1917         
1918         return this.el.select('.panel-body',true).first()
1919     },
1920     
1921     titleEl : function()
1922     {
1923         if(!this.el || !this.panel.length || !this.header.length){
1924             return;
1925         }
1926         
1927         return this.el.select('.panel-title',true).first();
1928     },
1929     
1930     setTitle : function(v)
1931     {
1932         var titleEl = this.titleEl();
1933         
1934         if(!titleEl){
1935             return;
1936         }
1937         
1938         titleEl.dom.innerHTML = v;
1939     },
1940     
1941     getTitle : function()
1942     {
1943         
1944         var titleEl = this.titleEl();
1945         
1946         if(!titleEl){
1947             return '';
1948         }
1949         
1950         return titleEl.dom.innerHTML;
1951     },
1952     
1953     setRightTitle : function(v)
1954     {
1955         var t = this.el.select('.panel-header-right',true).first();
1956         
1957         if(!t){
1958             return;
1959         }
1960         
1961         t.dom.innerHTML = v;
1962     },
1963     
1964     onClick : function(e)
1965     {
1966         e.preventDefault();
1967         
1968         this.fireEvent('click', this, e);
1969     }
1970 });
1971
1972  /**
1973  * @class Roo.bootstrap.Card
1974  * @extends Roo.bootstrap.Component
1975  * @children Roo.bootstrap.Component
1976  * @licence LGPL
1977  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1978  *
1979  *
1980  * possible... may not be implemented..
1981  * @cfg {String} header_image  src url of image.
1982  * @cfg {String|Object} header
1983  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1984  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1985  * 
1986  * @cfg {String} title
1987  * @cfg {String} subtitle
1988  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1989  * @cfg {String} footer
1990  
1991  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1992  * 
1993  * @cfg {String} margin (0|1|2|3|4|5|auto)
1994  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1995  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2000  *
2001  * @cfg {String} padding (0|1|2|3|4|5)
2002  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2003  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2004  * @cfg {String} padding_left (0|1|2|3|4|5)
2005  * @cfg {String} padding_right (0|1|2|3|4|5)
2006  * @cfg {String} padding_x (0|1|2|3|4|5)
2007  * @cfg {String} padding_y (0|1|2|3|4|5)
2008  *
2009  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2011  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  
2015  * @config {Boolean} dragable  if this card can be dragged.
2016  * @config {String} drag_group  group for drag
2017  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2018  * @config {String} drop_group  group for drag
2019  * 
2020  * @config {Boolean} collapsable can the body be collapsed.
2021  * @config {Boolean} collapsed is the body collapsed when rendered...
2022  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2023  * @config {Boolean} rotated is the body rotated when rendered...
2024  * 
2025  * @constructor
2026  * Create a new Container
2027  * @param {Object} config The config object
2028  */
2029
2030 Roo.bootstrap.Card = function(config){
2031     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2032     
2033     this.addEvents({
2034          // raw events
2035         /**
2036          * @event drop
2037          * When a element a card is dropped
2038          * @param {Roo.bootstrap.Card} this
2039          *
2040          * 
2041          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2042          * @param {String} position 'above' or 'below'
2043          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2044         
2045          */
2046         'drop' : true,
2047          /**
2048          * @event rotate
2049          * When a element a card is rotate
2050          * @param {Roo.bootstrap.Card} this
2051          * @param {Roo.Element} n the node being dropped?
2052          * @param {Boolean} rotate status
2053          */
2054         'rotate' : true,
2055         /**
2056          * @event cardover
2057          * When a card element is dragged over ready to drop (return false to block dropable)
2058          * @param {Roo.bootstrap.Card} this
2059          * @param {Object} data from dragdrop 
2060          */
2061          'cardover' : true
2062          
2063     });
2064 };
2065
2066
2067 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2068     
2069     
2070     weight : '',
2071     
2072     margin: '', /// may be better in component?
2073     margin_top: '', 
2074     margin_bottom: '', 
2075     margin_left: '',
2076     margin_right: '',
2077     margin_x: '',
2078     margin_y: '',
2079     
2080     padding : '',
2081     padding_top: '', 
2082     padding_bottom: '', 
2083     padding_left: '',
2084     padding_right: '',
2085     padding_x: '',
2086     padding_y: '',
2087     
2088     display: '', 
2089     display_xs: '', 
2090     display_sm: '', 
2091     display_lg: '',
2092     display_xl: '',
2093  
2094     header_image  : '',
2095     header : '',
2096     header_size : 0,
2097     title : '',
2098     subtitle : '',
2099     html : '',
2100     footer: '',
2101
2102     collapsable : false,
2103     collapsed : false,
2104     rotateable : false,
2105     rotated : false,
2106     
2107     dragable : false,
2108     drag_group : false,
2109     dropable : false,
2110     drop_group : false,
2111     childContainer : false,
2112     dropEl : false, /// the dom placeholde element that indicates drop location.
2113     containerEl: false, // body container
2114     bodyEl: false, // card-body
2115     headerContainerEl : false, //
2116     headerEl : false,
2117     header_imageEl : false,
2118     
2119     
2120     layoutCls : function()
2121     {
2122         var cls = '';
2123         var t = this;
2124         Roo.log(this.margin_bottom.length);
2125         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2126             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2127             
2128             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2130             }
2131             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2132                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2133             }
2134         });
2135         
2136         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2137             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2138                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2139             }
2140         });
2141         
2142         // more generic support?
2143         if (this.hidden) {
2144             cls += ' d-none';
2145         }
2146         
2147         return cls;
2148     },
2149  
2150        // Roo.log("Call onRender: " + this.xtype);
2151         /*  We are looking at something like this.
2152 <div class="card">
2153     <img src="..." class="card-img-top" alt="...">
2154     <div class="card-body">
2155         <h5 class="card-title">Card title</h5>
2156          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2157
2158         >> this bit is really the body...
2159         <div> << we will ad dthis in hopefully it will not break shit.
2160         
2161         ** card text does not actually have any styling...
2162         
2163             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2164         
2165         </div> <<
2166           <a href="#" class="card-link">Card link</a>
2167           
2168     </div>
2169     <div class="card-footer">
2170         <small class="text-muted">Last updated 3 mins ago</small>
2171     </div>
2172 </div>
2173          */
2174     getAutoCreate : function(){
2175         
2176         var cfg = {
2177             tag : 'div',
2178             cls : 'card',
2179             cn : [ ]
2180         };
2181         
2182         if (this.weight.length && this.weight != 'light') {
2183             cfg.cls += ' text-white';
2184         } else {
2185             cfg.cls += ' text-dark'; // need as it's nested..
2186         }
2187         if (this.weight.length) {
2188             cfg.cls += ' bg-' + this.weight;
2189         }
2190         
2191         cfg.cls += ' ' + this.layoutCls(); 
2192         
2193         var hdr = false;
2194         var hdr_ctr = false;
2195         if (this.header.length) {
2196             hdr = {
2197                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2198                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2199                 cn : []
2200             };
2201             cfg.cn.push(hdr);
2202             hdr_ctr = hdr;
2203         } else {
2204             hdr = {
2205                 tag : 'div',
2206                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2207                 cn : []
2208             };
2209             cfg.cn.push(hdr);
2210             hdr_ctr = hdr;
2211         }
2212         if (this.collapsable) {
2213             hdr_ctr = {
2214             tag : 'a',
2215             cls : 'd-block user-select-none',
2216             cn: [
2217                     {
2218                         tag: 'i',
2219                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2220                     }
2221                    
2222                 ]
2223             };
2224             hdr.cn.push(hdr_ctr);
2225         }
2226         
2227         hdr_ctr.cn.push(        {
2228             tag: 'span',
2229             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2230             html : this.header
2231         });
2232         
2233         
2234         if (this.header_image.length) {
2235             cfg.cn.push({
2236                 tag : 'img',
2237                 cls : 'card-img-top',
2238                 src: this.header_image // escape?
2239             });
2240         } else {
2241             cfg.cn.push({
2242                     tag : 'div',
2243                     cls : 'card-img-top d-none' 
2244                 });
2245         }
2246             
2247         var body = {
2248             tag : 'div',
2249             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2250             cn : []
2251         };
2252         var obody = body;
2253         if (this.collapsable || this.rotateable) {
2254             obody = {
2255                 tag: 'div',
2256                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2257                 cn : [  body ]
2258             };
2259         }
2260         
2261         cfg.cn.push(obody);
2262         
2263         if (this.title.length) {
2264             body.cn.push({
2265                 tag : 'div',
2266                 cls : 'card-title',
2267                 src: this.title // escape?
2268             });
2269         }  
2270         
2271         if (this.subtitle.length) {
2272             body.cn.push({
2273                 tag : 'div',
2274                 cls : 'card-title',
2275                 src: this.subtitle // escape?
2276             });
2277         }
2278         
2279         body.cn.push({
2280             tag : 'div',
2281             cls : 'roo-card-body-ctr'
2282         });
2283         
2284         if (this.html.length) {
2285             body.cn.push({
2286                 tag: 'div',
2287                 html : this.html
2288             });
2289         }
2290         // fixme ? handle objects?
2291         
2292         if (this.footer.length) {
2293            
2294             cfg.cn.push({
2295                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2296                 html : this.footer
2297             });
2298             
2299         } else {
2300             cfg.cn.push({cls : 'card-footer d-none'});
2301         }
2302         
2303         // footer...
2304         
2305         return cfg;
2306     },
2307     
2308     
2309     getCardHeader : function()
2310     {
2311         var  ret = this.el.select('.card-header',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardFooter : function()
2319     {
2320         var  ret = this.el.select('.card-footer',true).first();
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324         
2325         return ret;
2326     },
2327     getCardImageTop : function()
2328     {
2329         var  ret = this.header_imageEl;
2330         if (ret.hasClass('d-none')) {
2331             ret.removeClass('d-none');
2332         }
2333             
2334         return ret;
2335     },
2336     
2337     getChildContainer : function()
2338     {
2339         
2340         if(!this.el){
2341             return false;
2342         }
2343         return this.el.select('.roo-card-body-ctr',true).first();    
2344     },
2345     
2346     initEvents: function() 
2347     {
2348         this.bodyEl = this.el.select('.card-body',true).first(); 
2349         this.containerEl = this.getChildContainer();
2350         if(this.dragable){
2351             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2352                     containerScroll: true,
2353                     ddGroup: this.drag_group || 'default_card_drag_group'
2354             });
2355             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2356         }
2357         if (this.dropable) {
2358             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2359                 containerScroll: true,
2360                 ddGroup: this.drop_group || 'default_card_drag_group'
2361             });
2362             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2363             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2364             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2365             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2366             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2367         }
2368         
2369         if (this.collapsable) {
2370             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2371         }
2372         if (this.rotateable) {
2373             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2374         }
2375         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2376          
2377         this.footerEl = this.el.select('.card-footer',true).first();
2378         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2379         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2380         this.headerEl = this.el.select('.card-header',true).first();
2381         
2382         if (this.rotated) {
2383             this.el.addClass('roo-card-rotated');
2384             this.fireEvent('rotate', this, true);
2385         }
2386         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2387         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2388         
2389     },
2390     getDragData : function(e)
2391     {
2392         var target = this.getEl();
2393         if (target) {
2394             //this.handleSelection(e);
2395             
2396             var dragData = {
2397                 source: this,
2398                 copy: false,
2399                 nodes: this.getEl(),
2400                 records: []
2401             };
2402             
2403             
2404             dragData.ddel = target.dom ;    // the div element
2405             Roo.log(target.getWidth( ));
2406             dragData.ddel.style.width = target.getWidth() + 'px';
2407             
2408             return dragData;
2409         }
2410         return false;
2411     },
2412     /**
2413     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2414     *    whole Element becomes the target, and this causes the drop gesture to append.
2415     *
2416     *    Returns an object:
2417     *     {
2418            
2419            position : 'below' or 'above'
2420            card  : relateive to card OBJECT (or true for no cards listed)
2421            items_n : relative to nth item in list
2422            card_n : relative to  nth card in list
2423     }
2424     *
2425     *    
2426     */
2427     getTargetFromEvent : function(e, dragged_card_el)
2428     {
2429         var target = e.getTarget();
2430         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2431             target = target.parentNode;
2432         }
2433         
2434         var ret = {
2435             position: '',
2436             cards : [],
2437             card_n : -1,
2438             items_n : -1,
2439             card : false 
2440         };
2441         
2442         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2443         // see if target is one of the 'cards'...
2444         
2445         
2446         //Roo.log(this.items.length);
2447         var pos = false;
2448         
2449         var last_card_n = 0;
2450         var cards_len  = 0;
2451         for (var i = 0;i< this.items.length;i++) {
2452             
2453             if (!this.items[i].el.hasClass('card')) {
2454                  continue;
2455             }
2456             pos = this.getDropPoint(e, this.items[i].el.dom);
2457             
2458             cards_len = ret.cards.length;
2459             //Roo.log(this.items[i].el.dom.id);
2460             ret.cards.push(this.items[i]);
2461             last_card_n  = i;
2462             if (ret.card_n < 0 && pos == 'above') {
2463                 ret.position = cards_len > 0 ? 'below' : pos;
2464                 ret.items_n = i > 0 ? i - 1 : 0;
2465                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2466                 ret.card = ret.cards[ret.card_n];
2467             }
2468         }
2469         if (!ret.cards.length) {
2470             ret.card = true;
2471             ret.position = 'below';
2472             ret.items_n;
2473             return ret;
2474         }
2475         // could not find a card.. stick it at the end..
2476         if (ret.card_n < 0) {
2477             ret.card_n = last_card_n;
2478             ret.card = ret.cards[last_card_n];
2479             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2480             ret.position = 'below';
2481         }
2482         
2483         if (this.items[ret.items_n].el == dragged_card_el) {
2484             return false;
2485         }
2486         
2487         if (ret.position == 'below') {
2488             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2489             
2490             if (card_after  && card_after.el == dragged_card_el) {
2491                 return false;
2492             }
2493             return ret;
2494         }
2495         
2496         // its's after ..
2497         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2498         
2499         if (card_before  && card_before.el == dragged_card_el) {
2500             return false;
2501         }
2502         
2503         return ret;
2504     },
2505     
2506     onNodeEnter : function(n, dd, e, data){
2507         return false;
2508     },
2509     onNodeOver : function(n, dd, e, data)
2510     {
2511        
2512         var target_info = this.getTargetFromEvent(e,data.source.el);
2513         if (target_info === false) {
2514             this.dropPlaceHolder('hide');
2515             return false;
2516         }
2517         Roo.log(['getTargetFromEvent', target_info ]);
2518         
2519         
2520         if (this.fireEvent('cardover', this, [ data ]) === false) {
2521             return false;
2522         }
2523         
2524         this.dropPlaceHolder('show', target_info,data);
2525         
2526         return false; 
2527     },
2528     onNodeOut : function(n, dd, e, data){
2529         this.dropPlaceHolder('hide');
2530      
2531     },
2532     onNodeDrop : function(n, dd, e, data)
2533     {
2534         
2535         // call drop - return false if
2536         
2537         // this could actually fail - if the Network drops..
2538         // we will ignore this at present..- client should probably reload
2539         // the whole set of cards if stuff like that fails.
2540         
2541         
2542         var info = this.getTargetFromEvent(e,data.source.el);
2543         if (info === false) {
2544             return false;
2545         }
2546         this.dropPlaceHolder('hide');
2547   
2548           
2549     
2550         this.acceptCard(data.source, info.position, info.card, info.items_n);
2551         return true;
2552          
2553     },
2554     firstChildCard : function()
2555     {
2556         for (var i = 0;i< this.items.length;i++) {
2557             
2558             if (!this.items[i].el.hasClass('card')) {
2559                  continue;
2560             }
2561             return this.items[i];
2562         }
2563         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2564     },
2565     /**
2566      * accept card
2567      *
2568      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2569      */
2570     acceptCard : function(move_card,  position, next_to_card )
2571     {
2572         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2573             return false;
2574         }
2575         
2576         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2577         
2578         move_card.parent().removeCard(move_card);
2579         
2580         
2581         var dom = move_card.el.dom;
2582         dom.style.width = ''; // clear with - which is set by drag.
2583         
2584         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2585             var cardel = next_to_card.el.dom;
2586             
2587             if (position == 'above' ) {
2588                 cardel.parentNode.insertBefore(dom, cardel);
2589             } else if (cardel.nextSibling) {
2590                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2591             } else {
2592                 cardel.parentNode.append(dom);
2593             }
2594         } else {
2595             // card container???
2596             this.containerEl.dom.append(dom);
2597         }
2598         
2599         //FIXME HANDLE card = true 
2600         
2601         // add this to the correct place in items.
2602         
2603         // remove Card from items.
2604         
2605        
2606         if (this.items.length) {
2607             var nitems = [];
2608             //Roo.log([info.items_n, info.position, this.items.length]);
2609             for (var i =0; i < this.items.length; i++) {
2610                 if (i == to_items_n && position == 'above') {
2611                     nitems.push(move_card);
2612                 }
2613                 nitems.push(this.items[i]);
2614                 if (i == to_items_n && position == 'below') {
2615                     nitems.push(move_card);
2616                 }
2617             }
2618             this.items = nitems;
2619             Roo.log(this.items);
2620         } else {
2621             this.items.push(move_card);
2622         }
2623         
2624         move_card.parentId = this.id;
2625         
2626         return true;
2627         
2628         
2629     },
2630     removeCard : function(c)
2631     {
2632         this.items = this.items.filter(function(e) { return e != c });
2633  
2634         var dom = c.el.dom;
2635         dom.parentNode.removeChild(dom);
2636         dom.style.width = ''; // clear with - which is set by drag.
2637         c.parentId = false;
2638         
2639     },
2640     
2641     /**    Decide whether to drop above or below a View node. */
2642     getDropPoint : function(e, n, dd)
2643     {
2644         if (dd) {
2645              return false;
2646         }
2647         if (n == this.containerEl.dom) {
2648             return "above";
2649         }
2650         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2651         var c = t + (b - t) / 2;
2652         var y = Roo.lib.Event.getPageY(e);
2653         if(y <= c) {
2654             return "above";
2655         }else{
2656             return "below";
2657         }
2658     },
2659     onToggleCollapse : function(e)
2660         {
2661         if (this.collapsed) {
2662             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2663             this.collapsableEl.addClass('show');
2664             this.collapsed = false;
2665             return;
2666         }
2667         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2668         this.collapsableEl.removeClass('show');
2669         this.collapsed = true;
2670         
2671     
2672     },
2673     
2674     onToggleRotate : function(e)
2675     {
2676         this.collapsableEl.removeClass('show');
2677         this.footerEl.removeClass('d-none');
2678         this.el.removeClass('roo-card-rotated');
2679         this.el.removeClass('d-none');
2680         if (this.rotated) {
2681             
2682             this.collapsableEl.addClass('show');
2683             this.rotated = false;
2684             this.fireEvent('rotate', this, this.rotated);
2685             return;
2686         }
2687         this.el.addClass('roo-card-rotated');
2688         this.footerEl.addClass('d-none');
2689         this.el.select('.roo-collapsable').removeClass('show');
2690         
2691         this.rotated = true;
2692         this.fireEvent('rotate', this, this.rotated);
2693     
2694     },
2695     
2696     dropPlaceHolder: function (action, info, data)
2697     {
2698         if (this.dropEl === false) {
2699             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2700             cls : 'd-none'
2701             },true);
2702         }
2703         this.dropEl.removeClass(['d-none', 'd-block']);        
2704         if (action == 'hide') {
2705             
2706             this.dropEl.addClass('d-none');
2707             return;
2708         }
2709         // FIXME - info.card == true!!!
2710         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2711         
2712         if (info.card !== true) {
2713             var cardel = info.card.el.dom;
2714             
2715             if (info.position == 'above') {
2716                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2717             } else if (cardel.nextSibling) {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2719             } else {
2720                 cardel.parentNode.append(this.dropEl.dom);
2721             }
2722         } else {
2723             // card container???
2724             this.containerEl.dom.append(this.dropEl.dom);
2725         }
2726         
2727         this.dropEl.addClass('d-block roo-card-dropzone');
2728         
2729         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2730         
2731         
2732     
2733     
2734     
2735     },
2736     setHeaderText: function(html)
2737     {
2738         this.header = html;
2739         if (this.headerContainerEl) {
2740             this.headerContainerEl.dom.innerHTML = html;
2741         }
2742     },
2743     onHeaderImageLoad : function(ev, he)
2744     {
2745         if (!this.header_image_fit_square) {
2746             return;
2747         }
2748         
2749         var hw = he.naturalHeight / he.naturalWidth;
2750         // wide image = < 0
2751         // tall image = > 1
2752         //var w = he.dom.naturalWidth;
2753         var ww = he.width;
2754         he.style.left =  0;
2755         he.style.position =  'relative';
2756         if (hw > 1) {
2757             var nw = (ww * (1/hw));
2758             Roo.get(he).setSize( ww * (1/hw),  ww);
2759             he.style.left =  ((ww - nw)/ 2) + 'px';
2760             he.style.position =  'relative';
2761         }
2762
2763     }
2764
2765     
2766 });
2767
2768 /*
2769  * - LGPL
2770  *
2771  * Card header - holder for the card header elements.
2772  * 
2773  */
2774
2775 /**
2776  * @class Roo.bootstrap.CardHeader
2777  * @extends Roo.bootstrap.Element
2778  * @parent Roo.bootstrap.Card
2779  * @children Roo.bootstrap.Component
2780  * Bootstrap CardHeader class
2781  * @constructor
2782  * Create a new Card Header - that you can embed children into
2783  * @param {Object} config The config object
2784  */
2785
2786 Roo.bootstrap.CardHeader = function(config){
2787     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2788 };
2789
2790 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2791     
2792     
2793     container_method : 'getCardHeader' 
2794     
2795      
2796     
2797     
2798    
2799 });
2800
2801  
2802
2803  /*
2804  * - LGPL
2805  *
2806  * Card footer - holder for the card footer elements.
2807  * 
2808  */
2809
2810 /**
2811  * @class Roo.bootstrap.CardFooter
2812  * @extends Roo.bootstrap.Element
2813  * @parent Roo.bootstrap.Card
2814  * @children Roo.bootstrap.Component
2815  * Bootstrap CardFooter class
2816  * 
2817  * @constructor
2818  * Create a new Card Footer - that you can embed children into
2819  * @param {Object} config The config object
2820  */
2821
2822 Roo.bootstrap.CardFooter = function(config){
2823     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2824 };
2825
2826 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2827     
2828     
2829     container_method : 'getCardFooter' 
2830     
2831      
2832     
2833     
2834    
2835 });
2836
2837  
2838
2839  /*
2840  * - LGPL
2841  *
2842  * Card header - holder for the card header elements.
2843  * 
2844  */
2845
2846 /**
2847  * @class Roo.bootstrap.CardImageTop
2848  * @extends Roo.bootstrap.Element
2849  * @parent Roo.bootstrap.Card
2850  * @children Roo.bootstrap.Component
2851  * Bootstrap CardImageTop class
2852  * 
2853  * @constructor
2854  * Create a new Card Image Top container
2855  * @param {Object} config The config object
2856  */
2857
2858 Roo.bootstrap.CardImageTop = function(config){
2859     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2860 };
2861
2862 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2863     
2864    
2865     container_method : 'getCardImageTop' 
2866     
2867      
2868     
2869    
2870 });
2871
2872  
2873
2874  
2875 /*
2876 * Licence: LGPL
2877 */
2878
2879 /**
2880  * @class Roo.bootstrap.ButtonUploader
2881  * @extends Roo.bootstrap.Button
2882  * Bootstrap Button Uploader class - it's a button which when you add files to it
2883  *
2884  * 
2885  * @cfg {Number} errorTimeout default 3000
2886  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2887  * @cfg {Array}  html The button text.
2888  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2889  *
2890  * @constructor
2891  * Create a new CardUploader
2892  * @param {Object} config The config object
2893  */
2894
2895 Roo.bootstrap.ButtonUploader = function(config){
2896     
2897  
2898     
2899     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2900     
2901      
2902      this.addEvents({
2903          // raw events
2904         /**
2905          * @event beforeselect
2906          * When button is pressed, before show upload files dialog is shown
2907          * @param {Roo.bootstrap.UploaderButton} this
2908          *
2909          */
2910         'beforeselect' : true,
2911          /**
2912          * @event fired when files have been selected, 
2913          * When a the download link is clicked
2914          * @param {Roo.bootstrap.UploaderButton} this
2915          * @param {Array} Array of files that have been uploaded
2916          */
2917         'uploaded' : true
2918         
2919     });
2920 };
2921  
2922 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2923     
2924      
2925     errorTimeout : 3000,
2926      
2927     images : false,
2928    
2929     fileCollection : false,
2930     allowBlank : true,
2931     
2932     multiple : true,
2933     
2934     getAutoCreate : function()
2935     {
2936         var im = {
2937             tag: 'input',
2938             type : 'file',
2939             cls : 'd-none  roo-card-upload-selector' 
2940           
2941         };
2942         if (this.multiple) {
2943             im.multiple = 'multiple';
2944         }
2945         
2946         return  {
2947             cls :'div' ,
2948             cn : [
2949                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2950                 im
2951
2952             ]
2953         };
2954            
2955          
2956     },
2957      
2958    
2959     initEvents : function()
2960     {
2961         
2962         Roo.bootstrap.Button.prototype.initEvents.call(this);
2963         
2964         
2965         
2966         
2967         
2968         this.urlAPI = (window.createObjectURL && window) || 
2969                                 (window.URL && URL.revokeObjectURL && URL) || 
2970                                 (window.webkitURL && webkitURL);
2971                         
2972          
2973          
2974          
2975         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * Bootstrap Header class
3384  * @cfg {String} html content of header
3385  * @cfg {Number} level (1|2|3|4|5|6) default 1
3386  * 
3387  * @constructor
3388  * Create a new Header
3389  * @param {Object} config The config object
3390  */
3391
3392
3393 Roo.bootstrap.Header  = function(config){
3394     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3395 };
3396
3397 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3398     
3399     //href : false,
3400     html : false,
3401     level : 1,
3402     
3403     
3404     
3405     getAutoCreate : function(){
3406         
3407         
3408         
3409         var cfg = {
3410             tag: 'h' + (1 *this.level),
3411             html: this.html || ''
3412         } ;
3413         
3414         return cfg;
3415     }
3416    
3417 });
3418
3419  
3420
3421  Roo.bootstrap.menu = Roo.bootstrap.menu || {};
3422 // deprciated 
3423 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
3424 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
3425 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
3426
3427 /**
3428  * @class Roo.bootstrap.MenuMgr
3429  * @licence LGPL
3430  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3431  * @singleton
3432  */
3433 Roo.bootstrap.menu.Manager = function(){
3434    var menus, active, groups = {}, attached = false, lastShow = new Date();
3435
3436    // private - called when first menu is created
3437    function init(){
3438        menus = {};
3439        active = new Roo.util.MixedCollection();
3440        Roo.get(document).addKeyListener(27, function(){
3441            if(active.length > 0){
3442                hideAll();
3443            }
3444        });
3445    }
3446
3447    // private
3448    function hideAll(){
3449        if(active && active.length > 0){
3450            var c = active.clone();
3451            c.each(function(m){
3452                m.hide();
3453            });
3454        }
3455    }
3456
3457    // private
3458    function onHide(m){
3459        active.remove(m);
3460        if(active.length < 1){
3461            Roo.get(document).un("mouseup", onMouseDown);
3462             
3463            attached = false;
3464        }
3465    }
3466
3467    // private
3468    function onShow(m){
3469        var last = active.last();
3470        lastShow = new Date();
3471        active.add(m);
3472        if(!attached){
3473           Roo.get(document).on("mouseup", onMouseDown);
3474            
3475            attached = true;
3476        }
3477        if(m.parentMenu){
3478           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3479           m.parentMenu.activeChild = m;
3480        }else if(last && last.isVisible()){
3481           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3482        }
3483    }
3484
3485    // private
3486    function onBeforeHide(m){
3487        if(m.activeChild){
3488            m.activeChild.hide();
3489        }
3490        if(m.autoHideTimer){
3491            clearTimeout(m.autoHideTimer);
3492            delete m.autoHideTimer;
3493        }
3494    }
3495
3496    // private
3497    function onBeforeShow(m){
3498        var pm = m.parentMenu;
3499        if(!pm && !m.allowOtherMenus){
3500            hideAll();
3501        }else if(pm && pm.activeChild && active != m){
3502            pm.activeChild.hide();
3503        }
3504    }
3505
3506    // private this should really trigger on mouseup..
3507    function onMouseDown(e){
3508         Roo.log("on Mouse Up");
3509         
3510         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3511             Roo.log("MenuManager hideAll");
3512             hideAll();
3513             e.stopEvent();
3514         }
3515         
3516         
3517    }
3518
3519    // private
3520    function onBeforeCheck(mi, state){
3521        if(state){
3522            var g = groups[mi.group];
3523            for(var i = 0, l = g.length; i < l; i++){
3524                if(g[i] != mi){
3525                    g[i].setChecked(false);
3526                }
3527            }
3528        }
3529    }
3530
3531    return {
3532
3533        /**
3534         * Hides all menus that are currently visible
3535         */
3536        hideAll : function(){
3537             hideAll();  
3538        },
3539
3540        // private
3541        register : function(menu){
3542            if(!menus){
3543                init();
3544            }
3545            menus[menu.id] = menu;
3546            menu.on("beforehide", onBeforeHide);
3547            menu.on("hide", onHide);
3548            menu.on("beforeshow", onBeforeShow);
3549            menu.on("show", onShow);
3550            var g = menu.group;
3551            if(g && menu.events["checkchange"]){
3552                if(!groups[g]){
3553                    groups[g] = [];
3554                }
3555                groups[g].push(menu);
3556                menu.on("checkchange", onCheck);
3557            }
3558        },
3559
3560         /**
3561          * Returns a {@link Roo.menu.Menu} object
3562          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3563          * be used to generate and return a new Menu instance.
3564          */
3565        get : function(menu){
3566            if(typeof menu == "string"){ // menu id
3567                return menus[menu];
3568            }else if(menu.events){  // menu instance
3569                return menu;
3570            }
3571            /*else if(typeof menu.length == 'number'){ // array of menu items?
3572                return new Roo.bootstrap.Menu({items:menu});
3573            }else{ // otherwise, must be a config
3574                return new Roo.bootstrap.Menu(menu);
3575            }
3576            */
3577            return false;
3578        },
3579
3580        // private
3581        unregister : function(menu){
3582            delete menus[menu.id];
3583            menu.un("beforehide", onBeforeHide);
3584            menu.un("hide", onHide);
3585            menu.un("beforeshow", onBeforeShow);
3586            menu.un("show", onShow);
3587            var g = menu.group;
3588            if(g && menu.events["checkchange"]){
3589                groups[g].remove(menu);
3590                menu.un("checkchange", onCheck);
3591            }
3592        },
3593
3594        // private
3595        registerCheckable : function(menuItem){
3596            var g = menuItem.group;
3597            if(g){
3598                if(!groups[g]){
3599                    groups[g] = [];
3600                }
3601                groups[g].push(menuItem);
3602                menuItem.on("beforecheckchange", onBeforeCheck);
3603            }
3604        },
3605
3606        // private
3607        unregisterCheckable : function(menuItem){
3608            var g = menuItem.group;
3609            if(g){
3610                groups[g].remove(menuItem);
3611                menuItem.un("beforecheckchange", onBeforeCheck);
3612            }
3613        }
3614    };
3615 }(); 
3616 /**
3617  * @class Roo.bootstrap.menu.Menu
3618  * @extends Roo.bootstrap.Component
3619  * @licence LGPL
3620  * @children Roo.bootstrap.menu.Item
3621  * Bootstrap Menu class - container for MenuItems
3622  * 
3623  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3625  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3626  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3627   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3628   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3629  
3630  * @constructor
3631  * Create a new Menu
3632  * @param {Object} config The config objectQ
3633  */
3634
3635
3636 Roo.bootstrap.menu.Menu = function(config){
3637     
3638     if (config.type == 'treeview') {
3639         // normally menu's are drawn attached to the document to handle layering etc..
3640         // however treeview (used by the docs menu is drawn into the parent element)
3641         this.container_method = 'getChildContainer'; 
3642     }
3643     
3644     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645     if (this.registerMenu && this.type != 'treeview')  {
3646         Roo.bootstrap.menu.Manager.register(this);
3647     }
3648     
3649     
3650     this.addEvents({
3651         /**
3652          * @event beforeshow
3653          * Fires before this menu is displayed (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforeshow : true,
3657         /**
3658          * @event beforehide
3659          * Fires before this menu is hidden (return false to block)
3660          * @param {Roo.menu.Menu} this
3661          */
3662         beforehide : true,
3663         /**
3664          * @event show
3665          * Fires after this menu is displayed
3666          * @param {Roo.menu.Menu} this
3667          */
3668         show : true,
3669         /**
3670          * @event hide
3671          * Fires after this menu is hidden
3672          * @param {Roo.menu.Menu} this
3673          */
3674         hide : true,
3675         /**
3676          * @event click
3677          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680          * @param {Roo.EventObject} e
3681          */
3682         click : true,
3683         /**
3684          * @event mouseover
3685          * Fires when the mouse is hovering over this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseover : true,
3691         /**
3692          * @event mouseout
3693          * Fires when the mouse exits this menu
3694          * @param {Roo.menu.Menu} this
3695          * @param {Roo.EventObject} e
3696          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3697          */
3698         mouseout : true,
3699         /**
3700          * @event itemclick
3701          * Fires when a menu item contained in this menu is clicked
3702          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703          * @param {Roo.EventObject} e
3704          */
3705         itemclick: true
3706     });
3707     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 };
3709
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3711     
3712    /// html : false,
3713    
3714     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3715     type: false,
3716     /**
3717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718      */
3719     registerMenu : true,
3720     
3721     menuItems :false, // stores the menu items..
3722     
3723     hidden:true,
3724         
3725     parentMenu : false,
3726     
3727     stopEvent : true,
3728     
3729     isLink : false,
3730     
3731     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732     
3733     hideTrigger : false,
3734     
3735     align : 'tl-bl?',
3736     
3737     
3738     getChildContainer : function() {
3739         return this.el;  
3740     },
3741     
3742     getAutoCreate : function(){
3743          
3744         //if (['right'].indexOf(this.align)!==-1) {
3745         //    cfg.cn[1].cls += ' pull-right'
3746         //}
3747          
3748         var cfg = {
3749             tag : 'ul',
3750             cls : 'dropdown-menu shadow' ,
3751             style : 'z-index:1000'
3752             
3753         };
3754         
3755         if (this.type === 'submenu') {
3756             cfg.cls = 'submenu active';
3757         }
3758         if (this.type === 'treeview') {
3759             cfg.cls = 'treeview-menu';
3760         }
3761         
3762         return cfg;
3763     },
3764     initEvents : function() {
3765         
3766        // Roo.log("ADD event");
3767        // Roo.log(this.triggerEl.dom);
3768         if (this.triggerEl) {
3769             
3770             this.triggerEl.on('click', this.onTriggerClick, this);
3771             
3772             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773             
3774             if (!this.hideTrigger) {
3775                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776                     // dropdown toggle on the 'a' in BS4?
3777                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778                 } else {
3779                     this.triggerEl.addClass('dropdown-toggle');
3780                 }
3781             }
3782         }
3783         
3784         if (Roo.isTouch) {
3785             this.el.on('touchstart'  , this.onTouch, this);
3786         }
3787         this.el.on('click' , this.onClick, this);
3788
3789         this.el.on("mouseover", this.onMouseOver, this);
3790         this.el.on("mouseout", this.onMouseOut, this);
3791         
3792     },
3793     
3794     findTargetItem : function(e)
3795     {
3796         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3797         if(!t){
3798             return false;
3799         }
3800         //Roo.log(t);         Roo.log(t.id);
3801         if(t && t.id){
3802             //Roo.log(this.menuitems);
3803             return this.menuitems.get(t.id);
3804             
3805             //return this.items.get(t.menuItemId);
3806         }
3807         
3808         return false;
3809     },
3810     
3811     onTouch : function(e) 
3812     {
3813         Roo.log("menu.onTouch");
3814         //e.stopEvent(); this make the user popdown broken
3815         this.onClick(e);
3816     },
3817     
3818     onClick : function(e)
3819     {
3820         Roo.log("menu.onClick");
3821         
3822         var t = this.findTargetItem(e);
3823         if(!t || t.isContainer){
3824             return;
3825         }
3826         Roo.log(e);
3827         /*
3828         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3829             if(t == this.activeItem && t.shouldDeactivate(e)){
3830                 this.activeItem.deactivate();
3831                 delete this.activeItem;
3832                 return;
3833             }
3834             if(t.canActivate){
3835                 this.setActiveItem(t, true);
3836             }
3837             return;
3838             
3839             
3840         }
3841         */
3842        
3843         Roo.log('pass click event');
3844         
3845         t.onClick(e);
3846         
3847         this.fireEvent("click", this, t, e);
3848         
3849         var _this = this;
3850         
3851         if(!t.href.length || t.href == '#'){
3852             (function() { _this.hide(); }).defer(100);
3853         }
3854         
3855     },
3856     
3857     onMouseOver : function(e){
3858         var t  = this.findTargetItem(e);
3859         //Roo.log(t);
3860         //if(t){
3861         //    if(t.canActivate && !t.disabled){
3862         //        this.setActiveItem(t, true);
3863         //    }
3864         //}
3865         
3866         this.fireEvent("mouseover", this, e, t);
3867     },
3868     isVisible : function(){
3869         return !this.hidden;
3870     },
3871     onMouseOut : function(e){
3872         var t  = this.findTargetItem(e);
3873         
3874         //if(t ){
3875         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3876         //        this.activeItem.deactivate();
3877         //        delete this.activeItem;
3878         //    }
3879         //}
3880         this.fireEvent("mouseout", this, e, t);
3881     },
3882     
3883     
3884     /**
3885      * Displays this menu relative to another element
3886      * @param {String/HTMLElement/Roo.Element} element The element to align to
3887      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888      * the element (defaults to this.defaultAlign)
3889      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890      */
3891     show : function(el, pos, parentMenu)
3892     {
3893         if (false === this.fireEvent("beforeshow", this)) {
3894             Roo.log("show canceled");
3895             return;
3896         }
3897         this.parentMenu = parentMenu;
3898         if(!this.el){
3899             this.render();
3900         }
3901         this.el.addClass('show'); // show otherwise we do not know how big we are..
3902          
3903         var xy = this.el.getAlignToXY(el, pos);
3904         
3905         // bl-tl << left align  below
3906         // tl-bl << left align 
3907         
3908         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909             // if it goes to far to the right.. -> align left.
3910             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911         }
3912         if(xy[0] < 0){
3913             // was left align - go right?
3914             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915         }
3916         
3917         // goes down the bottom
3918         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919            xy[1]  < 0 ){
3920             var a = this.align.replace('?', '').split('-');
3921             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3922             
3923         }
3924         
3925         this.showAt(  xy , parentMenu, false);
3926     },
3927      /**
3928      * Displays this menu at a specific xy position
3929      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931      */
3932     showAt : function(xy, parentMenu, /* private: */_e){
3933         this.parentMenu = parentMenu;
3934         if(!this.el){
3935             this.render();
3936         }
3937         if(_e !== false){
3938             this.fireEvent("beforeshow", this);
3939             //xy = this.el.adjustForConstraints(xy);
3940         }
3941         
3942         //this.el.show();
3943         this.hideMenuItems();
3944         this.hidden = false;
3945         if (this.triggerEl) {
3946             this.triggerEl.addClass('open');
3947         }
3948         
3949         this.el.addClass('show');
3950         
3951         
3952         
3953         // reassign x when hitting right
3954         
3955         // reassign y when hitting bottom
3956         
3957         // but the list may align on trigger left or trigger top... should it be a properity?
3958         
3959         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3960             this.el.setXY(xy);
3961         }
3962         
3963         this.focus();
3964         this.fireEvent("show", this);
3965     },
3966     
3967     focus : function(){
3968         return;
3969         if(!this.hidden){
3970             this.doFocus.defer(50, this);
3971         }
3972     },
3973
3974     doFocus : function(){
3975         if(!this.hidden){
3976             this.focusEl.focus();
3977         }
3978     },
3979
3980     /**
3981      * Hides this menu and optionally all parent menus
3982      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983      */
3984     hide : function(deep)
3985     {
3986         if (false === this.fireEvent("beforehide", this)) {
3987             Roo.log("hide canceled");
3988             return;
3989         }
3990         this.hideMenuItems();
3991         if(this.el && this.isVisible()){
3992            
3993             if(this.activeItem){
3994                 this.activeItem.deactivate();
3995                 this.activeItem = null;
3996             }
3997             if (this.triggerEl) {
3998                 this.triggerEl.removeClass('open');
3999             }
4000             
4001             this.el.removeClass('show');
4002             this.hidden = true;
4003             this.fireEvent("hide", this);
4004         }
4005         if(deep === true && this.parentMenu){
4006             this.parentMenu.hide(true);
4007         }
4008     },
4009     
4010     onTriggerClick : function(e)
4011     {
4012         Roo.log('trigger click');
4013         
4014         var target = e.getTarget();
4015         
4016         Roo.log(target.nodeName.toLowerCase());
4017         
4018         if(target.nodeName.toLowerCase() === 'i'){
4019             e.preventDefault();
4020         }
4021         
4022     },
4023     
4024     onTriggerPress  : function(e)
4025     {
4026         Roo.log('trigger press');
4027         //Roo.log(e.getTarget());
4028        // Roo.log(this.triggerEl.dom);
4029        
4030         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031         var pel = Roo.get(e.getTarget());
4032         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033             Roo.log('is treeview or dropdown?');
4034             return;
4035         }
4036         
4037         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038             return;
4039         }
4040         
4041         if (this.isVisible()) {
4042             Roo.log('hide');
4043             this.hide();
4044         } else {
4045             Roo.log('show');
4046             
4047             this.show(this.triggerEl, this.align, false);
4048         }
4049         
4050         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4051             e.stopEvent();
4052         }
4053         
4054     },
4055        
4056     
4057     hideMenuItems : function()
4058     {
4059         Roo.log("hide Menu Items");
4060         if (!this.el) { 
4061             return;
4062         }
4063         
4064         this.el.select('.open',true).each(function(aa) {
4065             
4066             aa.removeClass('open');
4067          
4068         });
4069     },
4070     addxtypeChild : function (tree, cntr) {
4071         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072           
4073         this.menuitems.add(comp);
4074         return comp;
4075
4076     },
4077     getEl : function()
4078     {
4079         Roo.log(this.el);
4080         return this.el;
4081     },
4082     
4083     clear : function()
4084     {
4085         this.getEl().dom.innerHTML = '';
4086         this.menuitems.clear();
4087     }
4088 });
4089
4090  
4091   /**
4092  * @class Roo.bootstrap.menu.Item
4093  * @extends Roo.bootstrap.Component
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * @cfg {Boolean} submenu default false - 
4097  * @cfg {String} html text of the item
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} disabled  is the item disabled - default false 
4100  * @cfg {Boolean} preventDefault stop trigger click event to parent elements - default true
4101  * @cfg {String} fa  Font awesome icon
4102  * @cfg {String} pos (left|right) Submenu align to  default right 
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  
4105  * 
4106  * @constructor
4107  * Create a new Menu  Item
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         /**
4116          * @event mouseover
4117          * Fires when the mouse is hovering over this menu
4118          * @param {Roo.bootstrap.menu.Item} this
4119          * @param {Roo.EventObject} e
4120          */
4121         mouseover : true,
4122         /**
4123          * @event mouseout
4124          * Fires when the mouse exits this menu
4125          * @param {Roo.bootstrap.menu.Item} this
4126          * @param {Roo.EventObject} e
4127          */
4128         mouseout : true,
4129         // raw events
4130         /**
4131          * @event click
4132          * The raw click event for the entire grid.
4133          * @param {Roo.EventObject} e
4134          */
4135         click : true
4136     });
4137 };
4138
4139 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4140     
4141     submenu : false,
4142     href : '',
4143     html : '',
4144     preventDefault: true,
4145     disable : false,
4146     active : false,    
4147     fa : false,
4148     pos : 'right',
4149     
4150     isContainer : false, // ?? only a <li drowdonw-menu-item">
4151     
4152     getAutoCreate : function()
4153     {
4154         
4155         if(this.isContainer){
4156             return {
4157                 tag: 'li',
4158                 cls: 'dropdown-menu-item '
4159             };
4160         }
4161         
4162         var ctag = {
4163             tag: 'span',
4164             cls : 'roo-menu-item-text',
4165             html : this.html
4166         };
4167         
4168         var anc = {
4169             tag : 'a',
4170             cls : 'dropdown-item',
4171             href : this.href || '#',
4172             cn : [  ]
4173         };
4174         
4175         if (this.fa !== false) {
4176             anc.cn.push({
4177                 tag : 'i',
4178                 cls : 'fa fa-' + this.fa
4179             });
4180         }
4181         
4182         anc.cn.push(ctag);
4183         
4184         
4185         var cfg= {
4186             tag: 'li',
4187             cls: 'dropdown-menu-item',
4188             cn: [ anc ]
4189         };
4190         if (this.parent().type == 'treeview') {
4191             cfg.cls = 'treeview-menu';
4192         }
4193         if (this.active) {
4194             cfg.cls += ' active';
4195         }
4196         
4197         if(this.disabled){
4198             cfg.cls +=  ' disabled' 
4199         }
4200         
4201         if(this.submenu){
4202             cfg.cls +=  'dropdown-submenu';
4203             
4204             if(this.pos == 'left'){
4205                 cfg.cls +=  ' pull-left';
4206             }
4207         }
4208         anc.href = this.href || cfg.cn[0].href ;
4209         ctag.html = this.html || cfg.cn[0].html ;
4210         return cfg;
4211          
4212          
4213     },
4214     
4215     initEvents : function() 
4216     {
4217         if (this.parent().type == 'treeview') {
4218             this.el.select('a').on('click', this.onClick, this);
4219         }
4220         if (this.menu) {
4221             this.menu.parentType = this.xtype;
4222             this.menu.triggerEl = this.el;
4223             this.menu = this.addxtype(Roo.apply({}, this.menu));
4224         }
4225         this.el.on('mouseover', this.onMouseOver, this);
4226         this.el.on('mouseout', this.onMouseOut, this);
4227         
4228         this.el.select('a', true).first().on('click', this.onClick, this);
4229         
4230     },
4231     
4232     onClick : function(e)
4233     {
4234         if(this.preventDefault){
4235             e.preventDefault();
4236         }
4237         
4238         this.fireEvent("click", this, e);
4239     },
4240     
4241     onMouseOver : function(e)
4242     {
4243         if(this.submenu && this.pos == 'left'){
4244             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
4245         }
4246         
4247         this.fireEvent("mouseover", this, e);
4248     },
4249     
4250     onMouseOut : function(e)
4251     {
4252         this.fireEvent("mouseout", this, e);
4253     }
4254 });
4255
4256  
4257
4258   
4259 /**
4260  * @class Roo.bootstrap.menu.Separator
4261  * @extends Roo.bootstrap.Component
4262  * @licence LGPL
4263  * Bootstrap Separator class
4264  * 
4265  * @constructor
4266  * Create a new Separator
4267  * @param {Object} config The config object
4268  */
4269
4270
4271 Roo.bootstrap.menu.Separator = function(config){
4272     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4273 };
4274
4275 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4276     
4277     getAutoCreate : function(){
4278         var cfg = {
4279             tag : 'li',
4280             cls: 'dropdown-divider divider'
4281         };
4282         
4283         return cfg;
4284     }
4285    
4286 });
4287
4288  
4289
4290  
4291 /*
4292 * Licence: LGPL
4293 */
4294
4295 /**
4296  * @class Roo.bootstrap.Modal
4297  * @extends Roo.bootstrap.Component
4298  * @builder-top
4299  * @parent none
4300  * @children Roo.bootstrap.Component
4301  * Bootstrap Modal class
4302  * @cfg {String} title Title of dialog
4303  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4304  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4305  * @cfg {Boolean} specificTitle default false
4306  * @cfg {Array} buttons Array of buttons or standard button set..
4307  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4308  * @cfg {Boolean} animate default true
4309  * @cfg {Boolean} allow_close default true
4310  * @cfg {Boolean} fitwindow default false
4311  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4312  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4313  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4314  * @cfg {String} size (sm|lg|xl) default empty
4315  * @cfg {Number} max_width set the max width of modal
4316  * @cfg {Boolean} editableTitle can the title be edited
4317
4318  *
4319  *
4320  * @constructor
4321  * Create a new Modal Dialog
4322  * @param {Object} config The config object
4323  */
4324
4325 Roo.bootstrap.Modal = function(config){
4326     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4327     this.addEvents({
4328         // raw events
4329         /**
4330          * @event btnclick
4331          * The raw btnclick event for the button
4332          * @param {Roo.EventObject} e
4333          */
4334         "btnclick" : true,
4335         /**
4336          * @event resize
4337          * Fire when dialog resize
4338          * @param {Roo.bootstrap.Modal} this
4339          * @param {Roo.EventObject} e
4340          */
4341         "resize" : true,
4342         /**
4343          * @event titlechanged
4344          * Fire when the editable title has been changed
4345          * @param {Roo.bootstrap.Modal} this
4346          * @param {Roo.EventObject} value
4347          */
4348         "titlechanged" : true 
4349         
4350     });
4351     this.buttons = this.buttons || [];
4352
4353     if (this.tmpl) {
4354         this.tmpl = Roo.factory(this.tmpl);
4355     }
4356
4357 };
4358
4359 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4360
4361     title : 'test dialog',
4362
4363     buttons : false,
4364
4365     // set on load...
4366
4367     html: false,
4368
4369     tmp: false,
4370
4371     specificTitle: false,
4372
4373     buttonPosition: 'right',
4374
4375     allow_close : true,
4376
4377     animate : true,
4378
4379     fitwindow: false,
4380     
4381      // private
4382     dialogEl: false,
4383     bodyEl:  false,
4384     footerEl:  false,
4385     titleEl:  false,
4386     closeEl:  false,
4387
4388     size: '',
4389     
4390     max_width: 0,
4391     
4392     max_height: 0,
4393     
4394     fit_content: false,
4395     editableTitle  : false,
4396
4397     onRender : function(ct, position)
4398     {
4399         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4400
4401         if(!this.el){
4402             var cfg = Roo.apply({},  this.getAutoCreate());
4403             cfg.id = Roo.id();
4404             //if(!cfg.name){
4405             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4406             //}
4407             //if (!cfg.name.length) {
4408             //    delete cfg.name;
4409            // }
4410             if (this.cls) {
4411                 cfg.cls += ' ' + this.cls;
4412             }
4413             if (this.style) {
4414                 cfg.style = this.style;
4415             }
4416             this.el = Roo.get(document.body).createChild(cfg, position);
4417         }
4418         //var type = this.el.dom.type;
4419
4420
4421         if(this.tabIndex !== undefined){
4422             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4423         }
4424
4425         this.dialogEl = this.el.select('.modal-dialog',true).first();
4426         this.bodyEl = this.el.select('.modal-body',true).first();
4427         this.closeEl = this.el.select('.modal-header .close', true).first();
4428         this.headerEl = this.el.select('.modal-header',true).first();
4429         this.titleEl = this.el.select('.modal-title',true).first();
4430         this.footerEl = this.el.select('.modal-footer',true).first();
4431
4432         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4433         
4434         //this.el.addClass("x-dlg-modal");
4435
4436         if (this.buttons.length) {
4437             Roo.each(this.buttons, function(bb) {
4438                 var b = Roo.apply({}, bb);
4439                 b.xns = b.xns || Roo.bootstrap;
4440                 b.xtype = b.xtype || 'Button';
4441                 if (typeof(b.listeners) == 'undefined') {
4442                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4443                 }
4444
4445                 var btn = Roo.factory(b);
4446
4447                 btn.render(this.getButtonContainer());
4448
4449             },this);
4450         }
4451         // render the children.
4452         var nitems = [];
4453
4454         if(typeof(this.items) != 'undefined'){
4455             var items = this.items;
4456             delete this.items;
4457
4458             for(var i =0;i < items.length;i++) {
4459                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4460             }
4461         }
4462
4463         this.items = nitems;
4464
4465         // where are these used - they used to be body/close/footer
4466
4467
4468         this.initEvents();
4469         //this.el.addClass([this.fieldClass, this.cls]);
4470
4471     },
4472
4473     getAutoCreate : function()
4474     {
4475         // we will default to modal-body-overflow - might need to remove or make optional later.
4476         var bdy = {
4477                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4478                 html : this.html || ''
4479         };
4480
4481         var title = {
4482             tag: 'h5',
4483             cls : 'modal-title',
4484             html : this.title
4485         };
4486
4487         if(this.specificTitle){ // WTF is this?
4488             title = this.title;
4489         }
4490
4491         var header = [];
4492         if (this.allow_close && Roo.bootstrap.version == 3) {
4493             header.push({
4494                 tag: 'button',
4495                 cls : 'close',
4496                 html : '&times'
4497             });
4498         }
4499
4500         header.push(title);
4501
4502         if (this.editableTitle) {
4503             header.push({
4504                 cls: 'form-control roo-editable-title d-none',
4505                 tag: 'input',
4506                 type: 'text'
4507             });
4508         }
4509         
4510         if (this.allow_close && Roo.bootstrap.version == 4) {
4511             header.push({
4512                 tag: 'button',
4513                 cls : 'close',
4514                 html : '&times'
4515             });
4516         }
4517         
4518         var size = '';
4519
4520         if(this.size.length){
4521             size = 'modal-' + this.size;
4522         }
4523         
4524         var footer = Roo.bootstrap.version == 3 ?
4525             {
4526                 cls : 'modal-footer',
4527                 cn : [
4528                     {
4529                         tag: 'div',
4530                         cls: 'btn-' + this.buttonPosition
4531                     }
4532                 ]
4533
4534             } :
4535             {  // BS4 uses mr-auto on left buttons....
4536                 cls : 'modal-footer'
4537             };
4538
4539             
4540
4541         
4542         
4543         var modal = {
4544             cls: "modal",
4545              cn : [
4546                 {
4547                     cls: "modal-dialog " + size,
4548                     cn : [
4549                         {
4550                             cls : "modal-content",
4551                             cn : [
4552                                 {
4553                                     cls : 'modal-header',
4554                                     cn : header
4555                                 },
4556                                 bdy,
4557                                 footer
4558                             ]
4559
4560                         }
4561                     ]
4562
4563                 }
4564             ]
4565         };
4566
4567         if(this.animate){
4568             modal.cls += ' fade';
4569         }
4570
4571         return modal;
4572
4573     },
4574     getChildContainer : function() {
4575
4576          return this.bodyEl;
4577
4578     },
4579     getButtonContainer : function() {
4580         
4581          return Roo.bootstrap.version == 4 ?
4582             this.el.select('.modal-footer',true).first()
4583             : this.el.select('.modal-footer div',true).first();
4584
4585     },
4586     initEvents : function()
4587     {
4588         if (this.allow_close) {
4589             this.closeEl.on('click', this.hide, this);
4590         }
4591         Roo.EventManager.onWindowResize(this.resize, this, true);
4592         if (this.editableTitle) {
4593             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4594             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4595             this.headerEditEl.on('keyup', function(e) {
4596                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4597                         this.toggleHeaderInput(false)
4598                     }
4599                 }, this);
4600             this.headerEditEl.on('blur', function(e) {
4601                 this.toggleHeaderInput(false)
4602             },this);
4603         }
4604
4605     },
4606   
4607
4608     resize : function()
4609     {
4610         this.maskEl.setSize(
4611             Roo.lib.Dom.getViewWidth(true),
4612             Roo.lib.Dom.getViewHeight(true)
4613         );
4614         
4615         if (this.fitwindow) {
4616             
4617            this.dialogEl.setStyle( { 'max-width' : '100%' });
4618             this.setSize(
4619                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4620                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4621             );
4622             return;
4623         }
4624         
4625         if(this.max_width !== 0) {
4626             
4627             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4628             
4629             if(this.height) {
4630                 this.setSize(w, this.height);
4631                 return;
4632             }
4633             
4634             if(this.max_height) {
4635                 this.setSize(w,Math.min(
4636                     this.max_height,
4637                     Roo.lib.Dom.getViewportHeight(true) - 60
4638                 ));
4639                 
4640                 return;
4641             }
4642             
4643             if(!this.fit_content) {
4644                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4645                 return;
4646             }
4647             
4648             this.setSize(w, Math.min(
4649                 60 +
4650                 this.headerEl.getHeight() + 
4651                 this.footerEl.getHeight() + 
4652                 this.getChildHeight(this.bodyEl.dom.childNodes),
4653                 Roo.lib.Dom.getViewportHeight(true) - 60)
4654             );
4655         }
4656         
4657     },
4658
4659     setSize : function(w,h)
4660     {
4661         if (!w && !h) {
4662             return;
4663         }
4664         
4665         this.resizeTo(w,h);
4666     },
4667
4668     show : function() {
4669
4670         if (!this.rendered) {
4671             this.render();
4672         }
4673         this.toggleHeaderInput(false);
4674         //this.el.setStyle('display', 'block');
4675         this.el.removeClass('hideing');
4676         this.el.dom.style.display='block';
4677         
4678         Roo.get(document.body).addClass('modal-open');
4679  
4680         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4681             
4682             (function(){
4683                 this.el.addClass('show');
4684                 this.el.addClass('in');
4685             }).defer(50, this);
4686         }else{
4687             this.el.addClass('show');
4688             this.el.addClass('in');
4689         }
4690
4691         // not sure how we can show data in here..
4692         //if (this.tmpl) {
4693         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4694         //}
4695
4696         Roo.get(document.body).addClass("x-body-masked");
4697         
4698         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4699         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4700         this.maskEl.dom.style.display = 'block';
4701         this.maskEl.addClass('show');
4702         
4703         
4704         this.resize();
4705         
4706         this.fireEvent('show', this);
4707
4708         // set zindex here - otherwise it appears to be ignored...
4709         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4710
4711         (function () {
4712             this.items.forEach( function(e) {
4713                 e.layout ? e.layout() : false;
4714
4715             });
4716         }).defer(100,this);
4717
4718     },
4719     hide : function()
4720     {
4721         if(this.fireEvent("beforehide", this) !== false){
4722             
4723             this.maskEl.removeClass('show');
4724             
4725             this.maskEl.dom.style.display = '';
4726             Roo.get(document.body).removeClass("x-body-masked");
4727             this.el.removeClass('in');
4728             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4729
4730             if(this.animate){ // why
4731                 this.el.addClass('hideing');
4732                 this.el.removeClass('show');
4733                 (function(){
4734                     if (!this.el.hasClass('hideing')) {
4735                         return; // it's been shown again...
4736                     }
4737                     
4738                     this.el.dom.style.display='';
4739
4740                     Roo.get(document.body).removeClass('modal-open');
4741                     this.el.removeClass('hideing');
4742                 }).defer(150,this);
4743                 
4744             }else{
4745                 this.el.removeClass('show');
4746                 this.el.dom.style.display='';
4747                 Roo.get(document.body).removeClass('modal-open');
4748
4749             }
4750             this.fireEvent('hide', this);
4751         }
4752     },
4753     isVisible : function()
4754     {
4755         
4756         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4757         
4758     },
4759
4760     addButton : function(str, cb)
4761     {
4762
4763
4764         var b = Roo.apply({}, { html : str } );
4765         b.xns = b.xns || Roo.bootstrap;
4766         b.xtype = b.xtype || 'Button';
4767         if (typeof(b.listeners) == 'undefined') {
4768             b.listeners = { click : cb.createDelegate(this)  };
4769         }
4770
4771         var btn = Roo.factory(b);
4772
4773         btn.render(this.getButtonContainer());
4774
4775         return btn;
4776
4777     },
4778
4779     setDefaultButton : function(btn)
4780     {
4781         //this.el.select('.modal-footer').()
4782     },
4783
4784     resizeTo: function(w,h)
4785     {
4786         this.dialogEl.setWidth(w);
4787         
4788         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4789
4790         this.bodyEl.setHeight(h - diff);
4791         
4792         this.fireEvent('resize', this);
4793     },
4794     
4795     setContentSize  : function(w, h)
4796     {
4797
4798     },
4799     onButtonClick: function(btn,e)
4800     {
4801         //Roo.log([a,b,c]);
4802         this.fireEvent('btnclick', btn.name, e);
4803     },
4804      /**
4805      * Set the title of the Dialog
4806      * @param {String} str new Title
4807      */
4808     setTitle: function(str) {
4809         this.titleEl.dom.innerHTML = str;
4810         this.title = str;
4811     },
4812     /**
4813      * Set the body of the Dialog
4814      * @param {String} str new Title
4815      */
4816     setBody: function(str) {
4817         this.bodyEl.dom.innerHTML = str;
4818     },
4819     /**
4820      * Set the body of the Dialog using the template
4821      * @param {Obj} data - apply this data to the template and replace the body contents.
4822      */
4823     applyBody: function(obj)
4824     {
4825         if (!this.tmpl) {
4826             Roo.log("Error - using apply Body without a template");
4827             //code
4828         }
4829         this.tmpl.overwrite(this.bodyEl, obj);
4830     },
4831     
4832     getChildHeight : function(child_nodes)
4833     {
4834         if(
4835             !child_nodes ||
4836             child_nodes.length == 0
4837         ) {
4838             return 0;
4839         }
4840         
4841         var child_height = 0;
4842         
4843         for(var i = 0; i < child_nodes.length; i++) {
4844             
4845             /*
4846             * for modal with tabs...
4847             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4848                 
4849                 var layout_childs = child_nodes[i].childNodes;
4850                 
4851                 for(var j = 0; j < layout_childs.length; j++) {
4852                     
4853                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4854                         
4855                         var layout_body_childs = layout_childs[j].childNodes;
4856                         
4857                         for(var k = 0; k < layout_body_childs.length; k++) {
4858                             
4859                             if(layout_body_childs[k].classList.contains('navbar')) {
4860                                 child_height += layout_body_childs[k].offsetHeight;
4861                                 continue;
4862                             }
4863                             
4864                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4865                                 
4866                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4867                                 
4868                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4869                                     
4870                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4871                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4872                                         continue;
4873                                     }
4874                                     
4875                                 }
4876                                 
4877                             }
4878                             
4879                         }
4880                     }
4881                 }
4882                 continue;
4883             }
4884             */
4885             
4886             child_height += child_nodes[i].offsetHeight;
4887             // Roo.log(child_nodes[i].offsetHeight);
4888         }
4889         
4890         return child_height;
4891     },
4892     toggleHeaderInput : function(is_edit)
4893     {
4894         if (!this.editableTitle) {
4895             return; // not editable.
4896         }
4897         if (is_edit && this.is_header_editing) {
4898             return; // already editing..
4899         }
4900         if (is_edit) {
4901     
4902             this.headerEditEl.dom.value = this.title;
4903             this.headerEditEl.removeClass('d-none');
4904             this.headerEditEl.dom.focus();
4905             this.titleEl.addClass('d-none');
4906             
4907             this.is_header_editing = true;
4908             return
4909         }
4910         // flip back to not editing.
4911         this.title = this.headerEditEl.dom.value;
4912         this.headerEditEl.addClass('d-none');
4913         this.titleEl.removeClass('d-none');
4914         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4915         this.is_header_editing = false;
4916         this.fireEvent('titlechanged', this, this.title);
4917     
4918             
4919         
4920     }
4921
4922 });
4923
4924
4925 Roo.apply(Roo.bootstrap.Modal,  {
4926     /**
4927          * Button config that displays a single OK button
4928          * @type Object
4929          */
4930         OK :  [{
4931             name : 'ok',
4932             weight : 'primary',
4933             html : 'OK'
4934         }],
4935         /**
4936          * Button config that displays Yes and No buttons
4937          * @type Object
4938          */
4939         YESNO : [
4940             {
4941                 name  : 'no',
4942                 html : 'No'
4943             },
4944             {
4945                 name  :'yes',
4946                 weight : 'primary',
4947                 html : 'Yes'
4948             }
4949         ],
4950
4951         /**
4952          * Button config that displays OK and Cancel buttons
4953          * @type Object
4954          */
4955         OKCANCEL : [
4956             {
4957                name : 'cancel',
4958                 html : 'Cancel'
4959             },
4960             {
4961                 name : 'ok',
4962                 weight : 'primary',
4963                 html : 'OK'
4964             }
4965         ],
4966         /**
4967          * Button config that displays Yes, No and Cancel buttons
4968          * @type Object
4969          */
4970         YESNOCANCEL : [
4971             {
4972                 name : 'yes',
4973                 weight : 'primary',
4974                 html : 'Yes'
4975             },
4976             {
4977                 name : 'no',
4978                 html : 'No'
4979             },
4980             {
4981                 name : 'cancel',
4982                 html : 'Cancel'
4983             }
4984         ],
4985         
4986         zIndex : 10001
4987 });
4988
4989 /*
4990  * - LGPL
4991  *
4992  * messagebox - can be used as a replace
4993  * 
4994  */
4995 /**
4996  * @class Roo.MessageBox
4997  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4998  * Example usage:
4999  *<pre><code>
5000 // Basic alert:
5001 Roo.Msg.alert('Status', 'Changes saved successfully.');
5002
5003 // Prompt for user data:
5004 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
5005     if (btn == 'ok'){
5006         // process text value...
5007     }
5008 });
5009
5010 // Show a dialog using config options:
5011 Roo.Msg.show({
5012    title:'Save Changes?',
5013    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
5014    buttons: Roo.Msg.YESNOCANCEL,
5015    fn: processResult,
5016    animEl: 'elId'
5017 });
5018 </code></pre>
5019  * @singleton
5020  */
5021 Roo.bootstrap.MessageBox = function(){
5022     var dlg, opt, mask, waitTimer;
5023     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5024     var buttons, activeTextEl, bwidth;
5025
5026     
5027     // private
5028     var handleButton = function(button){
5029         dlg.hide();
5030         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5031     };
5032
5033     // private
5034     var handleHide = function(){
5035         if(opt && opt.cls){
5036             dlg.el.removeClass(opt.cls);
5037         }
5038         //if(waitTimer){
5039         //    Roo.TaskMgr.stop(waitTimer);
5040         //    waitTimer = null;
5041         //}
5042     };
5043
5044     // private
5045     var updateButtons = function(b){
5046         var width = 0;
5047         if(!b){
5048             buttons["ok"].hide();
5049             buttons["cancel"].hide();
5050             buttons["yes"].hide();
5051             buttons["no"].hide();
5052             dlg.footerEl.hide();
5053             
5054             return width;
5055         }
5056         dlg.footerEl.show();
5057         for(var k in buttons){
5058             if(typeof buttons[k] != "function"){
5059                 if(b[k]){
5060                     buttons[k].show();
5061                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5062                     width += buttons[k].el.getWidth()+15;
5063                 }else{
5064                     buttons[k].hide();
5065                 }
5066             }
5067         }
5068         return width;
5069     };
5070
5071     // private
5072     var handleEsc = function(d, k, e){
5073         if(opt && opt.closable !== false){
5074             dlg.hide();
5075         }
5076         if(e){
5077             e.stopEvent();
5078         }
5079     };
5080
5081     return {
5082         /**
5083          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5084          * @return {Roo.BasicDialog} The BasicDialog element
5085          */
5086         getDialog : function(){
5087            if(!dlg){
5088                 dlg = new Roo.bootstrap.Modal( {
5089                     //draggable: true,
5090                     //resizable:false,
5091                     //constraintoviewport:false,
5092                     //fixedcenter:true,
5093                     //collapsible : false,
5094                     //shim:true,
5095                     //modal: true,
5096                 //    width: 'auto',
5097                   //  height:100,
5098                     //buttonAlign:"center",
5099                     closeClick : function(){
5100                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5101                             handleButton("no");
5102                         }else{
5103                             handleButton("cancel");
5104                         }
5105                     }
5106                 });
5107                 dlg.render();
5108                 dlg.on("hide", handleHide);
5109                 mask = dlg.mask;
5110                 //dlg.addKeyListener(27, handleEsc);
5111                 buttons = {};
5112                 this.buttons = buttons;
5113                 var bt = this.buttonText;
5114                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5115                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5116                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5117                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5118                 //Roo.log(buttons);
5119                 bodyEl = dlg.bodyEl.createChild({
5120
5121                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5122                         '<textarea class="roo-mb-textarea"></textarea>' +
5123                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5124                 });
5125                 msgEl = bodyEl.dom.firstChild;
5126                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5127                 textboxEl.enableDisplayMode();
5128                 textboxEl.addKeyListener([10,13], function(){
5129                     if(dlg.isVisible() && opt && opt.buttons){
5130                         if(opt.buttons.ok){
5131                             handleButton("ok");
5132                         }else if(opt.buttons.yes){
5133                             handleButton("yes");
5134                         }
5135                     }
5136                 });
5137                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5138                 textareaEl.enableDisplayMode();
5139                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5140                 progressEl.enableDisplayMode();
5141                 
5142                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5143                 var pf = progressEl.dom.firstChild;
5144                 if (pf) {
5145                     pp = Roo.get(pf.firstChild);
5146                     pp.setHeight(pf.offsetHeight);
5147                 }
5148                 
5149             }
5150             return dlg;
5151         },
5152
5153         /**
5154          * Updates the message box body text
5155          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5156          * the XHTML-compliant non-breaking space character '&amp;#160;')
5157          * @return {Roo.MessageBox} This message box
5158          */
5159         updateText : function(text)
5160         {
5161             if(!dlg.isVisible() && !opt.width){
5162                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5163                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5164             }
5165             msgEl.innerHTML = text || '&#160;';
5166       
5167             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5168             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5169             var w = Math.max(
5170                     Math.min(opt.width || cw , this.maxWidth), 
5171                     Math.max(opt.minWidth || this.minWidth, bwidth)
5172             );
5173             if(opt.prompt){
5174                 activeTextEl.setWidth(w);
5175             }
5176             if(dlg.isVisible()){
5177                 dlg.fixedcenter = false;
5178             }
5179             // to big, make it scroll. = But as usual stupid IE does not support
5180             // !important..
5181             
5182             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5183                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5184                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5185             } else {
5186                 bodyEl.dom.style.height = '';
5187                 bodyEl.dom.style.overflowY = '';
5188             }
5189             if (cw > w) {
5190                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5191             } else {
5192                 bodyEl.dom.style.overflowX = '';
5193             }
5194             
5195             dlg.setContentSize(w, bodyEl.getHeight());
5196             if(dlg.isVisible()){
5197                 dlg.fixedcenter = true;
5198             }
5199             return this;
5200         },
5201
5202         /**
5203          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5204          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5205          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5206          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5207          * @return {Roo.MessageBox} This message box
5208          */
5209         updateProgress : function(value, text){
5210             if(text){
5211                 this.updateText(text);
5212             }
5213             
5214             if (pp) { // weird bug on my firefox - for some reason this is not defined
5215                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5216                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5217             }
5218             return this;
5219         },        
5220
5221         /**
5222          * Returns true if the message box is currently displayed
5223          * @return {Boolean} True if the message box is visible, else false
5224          */
5225         isVisible : function(){
5226             return dlg && dlg.isVisible();  
5227         },
5228
5229         /**
5230          * Hides the message box if it is displayed
5231          */
5232         hide : function(){
5233             if(this.isVisible()){
5234                 dlg.hide();
5235             }  
5236         },
5237
5238         /**
5239          * Displays a new message box, or reinitializes an existing message box, based on the config options
5240          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5241          * The following config object properties are supported:
5242          * <pre>
5243 Property    Type             Description
5244 ----------  ---------------  ------------------------------------------------------------------------------------
5245 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5246                                    closes (defaults to undefined)
5247 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5248                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5249 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5250                                    progress and wait dialogs will ignore this property and always hide the
5251                                    close button as they can only be closed programmatically.
5252 cls               String           A custom CSS class to apply to the message box element
5253 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5254                                    displayed (defaults to 75)
5255 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5256                                    function will be btn (the name of the button that was clicked, if applicable,
5257                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5258                                    Progress and wait dialogs will ignore this option since they do not respond to
5259                                    user actions and can only be closed programmatically, so any required function
5260                                    should be called by the same code after it closes the dialog.
5261 icon              String           A CSS class that provides a background image to be used as an icon for
5262                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5263 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5264 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5265 modal             Boolean          False to allow user interaction with the page while the message box is
5266                                    displayed (defaults to true)
5267 msg               String           A string that will replace the existing message box body text (defaults
5268                                    to the XHTML-compliant non-breaking space character '&#160;')
5269 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5270 progress          Boolean          True to display a progress bar (defaults to false)
5271 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5272 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5273 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5274 title             String           The title text
5275 value             String           The string value to set into the active textbox element if displayed
5276 wait              Boolean          True to display a progress bar (defaults to false)
5277 width             Number           The width of the dialog in pixels
5278 </pre>
5279          *
5280          * Example usage:
5281          * <pre><code>
5282 Roo.Msg.show({
5283    title: 'Address',
5284    msg: 'Please enter your address:',
5285    width: 300,
5286    buttons: Roo.MessageBox.OKCANCEL,
5287    multiline: true,
5288    fn: saveAddress,
5289    animEl: 'addAddressBtn'
5290 });
5291 </code></pre>
5292          * @param {Object} config Configuration options
5293          * @return {Roo.MessageBox} This message box
5294          */
5295         show : function(options)
5296         {
5297             
5298             // this causes nightmares if you show one dialog after another
5299             // especially on callbacks..
5300              
5301             if(this.isVisible()){
5302                 
5303                 this.hide();
5304                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5305                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5306                 Roo.log("New Dialog Message:" +  options.msg )
5307                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5308                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5309                 
5310             }
5311             var d = this.getDialog();
5312             opt = options;
5313             d.setTitle(opt.title || "&#160;");
5314             d.closeEl.setDisplayed(opt.closable !== false);
5315             activeTextEl = textboxEl;
5316             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5317             if(opt.prompt){
5318                 if(opt.multiline){
5319                     textboxEl.hide();
5320                     textareaEl.show();
5321                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5322                         opt.multiline : this.defaultTextHeight);
5323                     activeTextEl = textareaEl;
5324                 }else{
5325                     textboxEl.show();
5326                     textareaEl.hide();
5327                 }
5328             }else{
5329                 textboxEl.hide();
5330                 textareaEl.hide();
5331             }
5332             progressEl.setDisplayed(opt.progress === true);
5333             if (opt.progress) {
5334                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5335             }
5336             this.updateProgress(0);
5337             activeTextEl.dom.value = opt.value || "";
5338             if(opt.prompt){
5339                 dlg.setDefaultButton(activeTextEl);
5340             }else{
5341                 var bs = opt.buttons;
5342                 var db = null;
5343                 if(bs && bs.ok){
5344                     db = buttons["ok"];
5345                 }else if(bs && bs.yes){
5346                     db = buttons["yes"];
5347                 }
5348                 dlg.setDefaultButton(db);
5349             }
5350             bwidth = updateButtons(opt.buttons);
5351             this.updateText(opt.msg);
5352             if(opt.cls){
5353                 d.el.addClass(opt.cls);
5354             }
5355             d.proxyDrag = opt.proxyDrag === true;
5356             d.modal = opt.modal !== false;
5357             d.mask = opt.modal !== false ? mask : false;
5358             if(!d.isVisible()){
5359                 // force it to the end of the z-index stack so it gets a cursor in FF
5360                 document.body.appendChild(dlg.el.dom);
5361                 d.animateTarget = null;
5362                 d.show(options.animEl);
5363             }
5364             return this;
5365         },
5366
5367         /**
5368          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5369          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5370          * and closing the message box when the process is complete.
5371          * @param {String} title The title bar text
5372          * @param {String} msg The message box body text
5373          * @return {Roo.MessageBox} This message box
5374          */
5375         progress : function(title, msg){
5376             this.show({
5377                 title : title,
5378                 msg : msg,
5379                 buttons: false,
5380                 progress:true,
5381                 closable:false,
5382                 minWidth: this.minProgressWidth,
5383                 modal : true
5384             });
5385             return this;
5386         },
5387
5388         /**
5389          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5390          * If a callback function is passed it will be called after the user clicks the button, and the
5391          * id of the button that was clicked will be passed as the only parameter to the callback
5392          * (could also be the top-right close button).
5393          * @param {String} title The title bar text
5394          * @param {String} msg The message box body text
5395          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5396          * @param {Object} scope (optional) The scope of the callback function
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         alert : function(title, msg, fn, scope)
5400         {
5401             this.show({
5402                 title : title,
5403                 msg : msg,
5404                 buttons: this.OK,
5405                 fn: fn,
5406                 closable : false,
5407                 scope : scope,
5408                 modal : true
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5415          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5416          * You are responsible for closing the message box when the process is complete.
5417          * @param {String} msg The message box body text
5418          * @param {String} title (optional) The title bar text
5419          * @return {Roo.MessageBox} This message box
5420          */
5421         wait : function(msg, title){
5422             this.show({
5423                 title : title,
5424                 msg : msg,
5425                 buttons: false,
5426                 closable:false,
5427                 progress:true,
5428                 modal:true,
5429                 width:300,
5430                 wait:true
5431             });
5432             waitTimer = Roo.TaskMgr.start({
5433                 run: function(i){
5434                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5435                 },
5436                 interval: 1000
5437             });
5438             return this;
5439         },
5440
5441         /**
5442          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5443          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5444          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5445          * @param {String} title The title bar text
5446          * @param {String} msg The message box body text
5447          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5448          * @param {Object} scope (optional) The scope of the callback function
5449          * @return {Roo.MessageBox} This message box
5450          */
5451         confirm : function(title, msg, fn, scope){
5452             this.show({
5453                 title : title,
5454                 msg : msg,
5455                 buttons: this.YESNO,
5456                 fn: fn,
5457                 scope : scope,
5458                 modal : true
5459             });
5460             return this;
5461         },
5462
5463         /**
5464          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5465          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5466          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5467          * (could also be the top-right close button) and the text that was entered will be passed as the two
5468          * parameters to the callback.
5469          * @param {String} title The title bar text
5470          * @param {String} msg The message box body text
5471          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5472          * @param {Object} scope (optional) The scope of the callback function
5473          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5474          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5475          * @return {Roo.MessageBox} This message box
5476          */
5477         prompt : function(title, msg, fn, scope, multiline){
5478             this.show({
5479                 title : title,
5480                 msg : msg,
5481                 buttons: this.OKCANCEL,
5482                 fn: fn,
5483                 minWidth:250,
5484                 scope : scope,
5485                 prompt:true,
5486                 multiline: multiline,
5487                 modal : true
5488             });
5489             return this;
5490         },
5491
5492         /**
5493          * Button config that displays a single OK button
5494          * @type Object
5495          */
5496         OK : {ok:true},
5497         /**
5498          * Button config that displays Yes and No buttons
5499          * @type Object
5500          */
5501         YESNO : {yes:true, no:true},
5502         /**
5503          * Button config that displays OK and Cancel buttons
5504          * @type Object
5505          */
5506         OKCANCEL : {ok:true, cancel:true},
5507         /**
5508          * Button config that displays Yes, No and Cancel buttons
5509          * @type Object
5510          */
5511         YESNOCANCEL : {yes:true, no:true, cancel:true},
5512
5513         /**
5514          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5515          * @type Number
5516          */
5517         defaultTextHeight : 75,
5518         /**
5519          * The maximum width in pixels of the message box (defaults to 600)
5520          * @type Number
5521          */
5522         maxWidth : 600,
5523         /**
5524          * The minimum width in pixels of the message box (defaults to 100)
5525          * @type Number
5526          */
5527         minWidth : 100,
5528         /**
5529          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5530          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5531          * @type Number
5532          */
5533         minProgressWidth : 250,
5534         /**
5535          * An object containing the default button text strings that can be overriden for localized language support.
5536          * Supported properties are: ok, cancel, yes and no.
5537          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5538          * @type Object
5539          */
5540         buttonText : {
5541             ok : "OK",
5542             cancel : "Cancel",
5543             yes : "Yes",
5544             no : "No"
5545         }
5546     };
5547 }();
5548
5549 /**
5550  * Shorthand for {@link Roo.MessageBox}
5551  */
5552 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5553 Roo.Msg = Roo.Msg || Roo.MessageBox;
5554 /*
5555  * - LGPL
5556  *
5557  * navbar
5558  * 
5559  */
5560
5561 /**
5562  * @class Roo.bootstrap.Navbar
5563  * @extends Roo.bootstrap.Component
5564  * Bootstrap Navbar class
5565
5566  * @constructor
5567  * Create a new Navbar
5568  * @param {Object} config The config object
5569  */
5570
5571
5572 Roo.bootstrap.Navbar = function(config){
5573     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5574     this.addEvents({
5575         // raw events
5576         /**
5577          * @event beforetoggle
5578          * Fire before toggle the menu
5579          * @param {Roo.EventObject} e
5580          */
5581         "beforetoggle" : true
5582     });
5583 };
5584
5585 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5586     
5587     
5588    
5589     // private
5590     navItems : false,
5591     loadMask : false,
5592     
5593     
5594     getAutoCreate : function(){
5595         
5596         
5597         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5598         
5599     },
5600     
5601     initEvents :function ()
5602     {
5603         //Roo.log(this.el.select('.navbar-toggle',true));
5604         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5605         
5606         var mark = {
5607             tag: "div",
5608             cls:"x-dlg-mask"
5609         };
5610         
5611         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5612         
5613         var size = this.el.getSize();
5614         this.maskEl.setSize(size.width, size.height);
5615         this.maskEl.enableDisplayMode("block");
5616         this.maskEl.hide();
5617         
5618         if(this.loadMask){
5619             this.maskEl.show();
5620         }
5621     },
5622     
5623     
5624     getChildContainer : function()
5625     {
5626         if (this.el && this.el.select('.collapse').getCount()) {
5627             return this.el.select('.collapse',true).first();
5628         }
5629         
5630         return this.el;
5631     },
5632     
5633     mask : function()
5634     {
5635         this.maskEl.show();
5636     },
5637     
5638     unmask : function()
5639     {
5640         this.maskEl.hide();
5641     },
5642     onToggle : function()
5643     {
5644         
5645         if(this.fireEvent('beforetoggle', this) === false){
5646             return;
5647         }
5648         var ce = this.el.select('.navbar-collapse',true).first();
5649       
5650         if (!ce.hasClass('show')) {
5651            this.expand();
5652         } else {
5653             this.collapse();
5654         }
5655         
5656         
5657     
5658     },
5659     /**
5660      * Expand the navbar pulldown 
5661      */
5662     expand : function ()
5663     {
5664        
5665         var ce = this.el.select('.navbar-collapse',true).first();
5666         if (ce.hasClass('collapsing')) {
5667             return;
5668         }
5669         ce.dom.style.height = '';
5670                // show it...
5671         ce.addClass('in'); // old...
5672         ce.removeClass('collapse');
5673         ce.addClass('show');
5674         var h = ce.getHeight();
5675         Roo.log(h);
5676         ce.removeClass('show');
5677         // at this point we should be able to see it..
5678         ce.addClass('collapsing');
5679         
5680         ce.setHeight(0); // resize it ...
5681         ce.on('transitionend', function() {
5682             //Roo.log('done transition');
5683             ce.removeClass('collapsing');
5684             ce.addClass('show');
5685             ce.removeClass('collapse');
5686
5687             ce.dom.style.height = '';
5688         }, this, { single: true} );
5689         ce.setHeight(h);
5690         ce.dom.scrollTop = 0;
5691     },
5692     /**
5693      * Collapse the navbar pulldown 
5694      */
5695     collapse : function()
5696     {
5697          var ce = this.el.select('.navbar-collapse',true).first();
5698        
5699         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5700             // it's collapsed or collapsing..
5701             return;
5702         }
5703         ce.removeClass('in'); // old...
5704         ce.setHeight(ce.getHeight());
5705         ce.removeClass('show');
5706         ce.addClass('collapsing');
5707         
5708         ce.on('transitionend', function() {
5709             ce.dom.style.height = '';
5710             ce.removeClass('collapsing');
5711             ce.addClass('collapse');
5712         }, this, { single: true} );
5713         ce.setHeight(0);
5714     }
5715     
5716     
5717     
5718 });
5719
5720
5721
5722  
5723
5724  /*
5725  * - LGPL
5726  *
5727  * navbar
5728  * 
5729  */
5730
5731 /**
5732  * @class Roo.bootstrap.NavSimplebar
5733  * @extends Roo.bootstrap.Navbar
5734  * Bootstrap Sidebar class
5735  *
5736  * @cfg {Boolean} inverse is inverted color
5737  * 
5738  * @cfg {String} type (nav | pills | tabs)
5739  * @cfg {Boolean} arrangement stacked | justified
5740  * @cfg {String} align (left | right) alignment
5741  * 
5742  * @cfg {Boolean} main (true|false) main nav bar? default false
5743  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5744  * 
5745  * @cfg {String} tag (header|footer|nav|div) default is nav 
5746
5747  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5748  * 
5749  * 
5750  * @constructor
5751  * Create a new Sidebar
5752  * @param {Object} config The config object
5753  */
5754
5755
5756 Roo.bootstrap.NavSimplebar = function(config){
5757     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5761     
5762     inverse: false,
5763     
5764     type: false,
5765     arrangement: '',
5766     align : false,
5767     
5768     weight : 'light',
5769     
5770     main : false,
5771     
5772     
5773     tag : false,
5774     
5775     
5776     getAutoCreate : function(){
5777         
5778         
5779         var cfg = {
5780             tag : this.tag || 'div',
5781             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5782         };
5783         if (['light','white'].indexOf(this.weight) > -1) {
5784             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5785         }
5786         cfg.cls += ' bg-' + this.weight;
5787         
5788         if (this.inverse) {
5789             cfg.cls += ' navbar-inverse';
5790             
5791         }
5792         
5793         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5794         
5795         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5796             return cfg;
5797         }
5798         
5799         
5800     
5801         
5802         cfg.cn = [
5803             {
5804                 cls: 'nav nav-' + this.xtype,
5805                 tag : 'ul'
5806             }
5807         ];
5808         
5809          
5810         this.type = this.type || 'nav';
5811         if (['tabs','pills'].indexOf(this.type) != -1) {
5812             cfg.cn[0].cls += ' nav-' + this.type
5813         
5814         
5815         } else {
5816             if (this.type!=='nav') {
5817                 Roo.log('nav type must be nav/tabs/pills')
5818             }
5819             cfg.cn[0].cls += ' navbar-nav'
5820         }
5821         
5822         
5823         
5824         
5825         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5826             cfg.cn[0].cls += ' nav-' + this.arrangement;
5827         }
5828         
5829         
5830         if (this.align === 'right') {
5831             cfg.cn[0].cls += ' navbar-right';
5832         }
5833         
5834         
5835         
5836         
5837         return cfg;
5838     
5839         
5840     }
5841     
5842     
5843     
5844 });
5845
5846
5847
5848  
5849
5850  
5851        /*
5852  * - LGPL
5853  *
5854  * navbar
5855  * navbar-fixed-top
5856  * navbar-expand-md  fixed-top 
5857  */
5858
5859 /**
5860  * @class Roo.bootstrap.NavHeaderbar
5861  * @extends Roo.bootstrap.NavSimplebar
5862  * Bootstrap Sidebar class
5863  *
5864  * @cfg {String} brand what is brand
5865  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5866  * @cfg {String} brand_href href of the brand
5867  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5868  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5869  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5870  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5871  * 
5872  * @constructor
5873  * Create a new Sidebar
5874  * @param {Object} config The config object
5875  */
5876
5877
5878 Roo.bootstrap.NavHeaderbar = function(config){
5879     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5880       
5881 };
5882
5883 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5884     
5885     position: '',
5886     brand: '',
5887     brand_href: false,
5888     srButton : true,
5889     autohide : false,
5890     desktopCenter : false,
5891    
5892     
5893     getAutoCreate : function(){
5894         
5895         var   cfg = {
5896             tag: this.nav || 'nav',
5897             cls: 'navbar navbar-expand-md',
5898             role: 'navigation',
5899             cn: []
5900         };
5901         
5902         var cn = cfg.cn;
5903         if (this.desktopCenter) {
5904             cn.push({cls : 'container', cn : []});
5905             cn = cn[0].cn;
5906         }
5907         
5908         if(this.srButton){
5909             var btn = {
5910                 tag: 'button',
5911                 type: 'button',
5912                 cls: 'navbar-toggle navbar-toggler',
5913                 'data-toggle': 'collapse',
5914                 cn: [
5915                     {
5916                         tag: 'span',
5917                         cls: 'sr-only',
5918                         html: 'Toggle navigation'
5919                     },
5920                     {
5921                         tag: 'span',
5922                         cls: 'icon-bar navbar-toggler-icon'
5923                     },
5924                     {
5925                         tag: 'span',
5926                         cls: 'icon-bar'
5927                     },
5928                     {
5929                         tag: 'span',
5930                         cls: 'icon-bar'
5931                     }
5932                 ]
5933             };
5934             
5935             cn.push( Roo.bootstrap.version == 4 ? btn : {
5936                 tag: 'div',
5937                 cls: 'navbar-header',
5938                 cn: [
5939                     btn
5940                 ]
5941             });
5942         }
5943         
5944         cn.push({
5945             tag: 'div',
5946             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5947             cn : []
5948         });
5949         
5950         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5951         
5952         if (['light','white'].indexOf(this.weight) > -1) {
5953             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5954         }
5955         cfg.cls += ' bg-' + this.weight;
5956         
5957         
5958         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5959             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5960             
5961             // tag can override this..
5962             
5963             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5964         }
5965         
5966         if (this.brand !== '') {
5967             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5968             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5969                 tag: 'a',
5970                 href: this.brand_href ? this.brand_href : '#',
5971                 cls: 'navbar-brand',
5972                 cn: [
5973                 this.brand
5974                 ]
5975             });
5976         }
5977         
5978         if(this.main){
5979             cfg.cls += ' main-nav';
5980         }
5981         
5982         
5983         return cfg;
5984
5985         
5986     },
5987     getHeaderChildContainer : function()
5988     {
5989         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5990             return this.el.select('.navbar-header',true).first();
5991         }
5992         
5993         return this.getChildContainer();
5994     },
5995     
5996     getChildContainer : function()
5997     {
5998          
5999         return this.el.select('.roo-navbar-collapse',true).first();
6000          
6001         
6002     },
6003     
6004     initEvents : function()
6005     {
6006         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
6007         
6008         if (this.autohide) {
6009             
6010             var prevScroll = 0;
6011             var ft = this.el;
6012             
6013             Roo.get(document).on('scroll',function(e) {
6014                 var ns = Roo.get(document).getScroll().top;
6015                 var os = prevScroll;
6016                 prevScroll = ns;
6017                 
6018                 if(ns > os){
6019                     ft.removeClass('slideDown');
6020                     ft.addClass('slideUp');
6021                     return;
6022                 }
6023                 ft.removeClass('slideUp');
6024                 ft.addClass('slideDown');
6025                  
6026               
6027           },this);
6028         }
6029     }    
6030     
6031 });
6032
6033
6034
6035  
6036
6037  /*
6038  * - LGPL
6039  *
6040  * navbar
6041  * 
6042  */
6043
6044 /**
6045  * @class Roo.bootstrap.NavSidebar
6046  * @extends Roo.bootstrap.Navbar
6047  * Bootstrap Sidebar class
6048  * 
6049  * @constructor
6050  * Create a new Sidebar
6051  * @param {Object} config The config object
6052  */
6053
6054
6055 Roo.bootstrap.NavSidebar = function(config){
6056     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6057 };
6058
6059 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6060     
6061     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6062     
6063     getAutoCreate : function(){
6064         
6065         
6066         return  {
6067             tag: 'div',
6068             cls: 'sidebar sidebar-nav'
6069         };
6070     
6071         
6072     }
6073     
6074     
6075     
6076 });
6077
6078
6079
6080  
6081
6082  /*
6083  * - LGPL
6084  *
6085  * nav group
6086  * 
6087  */
6088
6089 /**
6090  * @class Roo.bootstrap.NavGroup
6091  * @extends Roo.bootstrap.Component
6092  * Bootstrap NavGroup class
6093  * @cfg {String} align (left|right)
6094  * @cfg {Boolean} inverse
6095  * @cfg {String} type (nav|pills|tab) default nav
6096  * @cfg {String} navId - reference Id for navbar.
6097  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6098  * 
6099  * @constructor
6100  * Create a new nav group
6101  * @param {Object} config The config object
6102  */
6103
6104 Roo.bootstrap.NavGroup = function(config){
6105     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6106     this.navItems = [];
6107    
6108     Roo.bootstrap.NavGroup.register(this);
6109      this.addEvents({
6110         /**
6111              * @event changed
6112              * Fires when the active item changes
6113              * @param {Roo.bootstrap.NavGroup} this
6114              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6115              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6116          */
6117         'changed': true
6118      });
6119     
6120 };
6121
6122 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6123     
6124     align: '',
6125     inverse: false,
6126     form: false,
6127     type: 'nav',
6128     navId : '',
6129     // private
6130     pilltype : true,
6131     
6132     navItems : false, 
6133     
6134     getAutoCreate : function()
6135     {
6136         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6137         
6138         cfg = {
6139             tag : 'ul',
6140             cls: 'nav' 
6141         };
6142         if (Roo.bootstrap.version == 4) {
6143             if (['tabs','pills'].indexOf(this.type) != -1) {
6144                 cfg.cls += ' nav-' + this.type; 
6145             } else {
6146                 // trying to remove so header bar can right align top?
6147                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6148                     // do not use on header bar... 
6149                     cfg.cls += ' navbar-nav';
6150                 }
6151             }
6152             
6153         } else {
6154             if (['tabs','pills'].indexOf(this.type) != -1) {
6155                 cfg.cls += ' nav-' + this.type
6156             } else {
6157                 if (this.type !== 'nav') {
6158                     Roo.log('nav type must be nav/tabs/pills')
6159                 }
6160                 cfg.cls += ' navbar-nav'
6161             }
6162         }
6163         
6164         if (this.parent() && this.parent().sidebar) {
6165             cfg = {
6166                 tag: 'ul',
6167                 cls: 'dashboard-menu sidebar-menu'
6168             };
6169             
6170             return cfg;
6171         }
6172         
6173         if (this.form === true) {
6174             cfg = {
6175                 tag: 'form',
6176                 cls: 'navbar-form form-inline'
6177             };
6178             //nav navbar-right ml-md-auto
6179             if (this.align === 'right') {
6180                 cfg.cls += ' navbar-right ml-md-auto';
6181             } else {
6182                 cfg.cls += ' navbar-left';
6183             }
6184         }
6185         
6186         if (this.align === 'right') {
6187             cfg.cls += ' navbar-right ml-md-auto';
6188         } else {
6189             cfg.cls += ' mr-auto';
6190         }
6191         
6192         if (this.inverse) {
6193             cfg.cls += ' navbar-inverse';
6194             
6195         }
6196         
6197         
6198         return cfg;
6199     },
6200     /**
6201     * sets the active Navigation item
6202     * @param {Roo.bootstrap.NavItem} the new current navitem
6203     */
6204     setActiveItem : function(item)
6205     {
6206         var prev = false;
6207         Roo.each(this.navItems, function(v){
6208             if (v == item) {
6209                 return ;
6210             }
6211             if (v.isActive()) {
6212                 v.setActive(false, true);
6213                 prev = v;
6214                 
6215             }
6216             
6217         });
6218
6219         item.setActive(true, true);
6220         this.fireEvent('changed', this, item, prev);
6221         
6222         
6223     },
6224     /**
6225     * gets the active Navigation item
6226     * @return {Roo.bootstrap.NavItem} the current navitem
6227     */
6228     getActive : function()
6229     {
6230         
6231         var prev = false;
6232         Roo.each(this.navItems, function(v){
6233             
6234             if (v.isActive()) {
6235                 prev = v;
6236                 
6237             }
6238             
6239         });
6240         return prev;
6241     },
6242     
6243     indexOfNav : function()
6244     {
6245         
6246         var prev = false;
6247         Roo.each(this.navItems, function(v,i){
6248             
6249             if (v.isActive()) {
6250                 prev = i;
6251                 
6252             }
6253             
6254         });
6255         return prev;
6256     },
6257     /**
6258     * adds a Navigation item
6259     * @param {Roo.bootstrap.NavItem} the navitem to add
6260     */
6261     addItem : function(cfg)
6262     {
6263         if (this.form && Roo.bootstrap.version == 4) {
6264             cfg.tag = 'div';
6265         }
6266         var cn = new Roo.bootstrap.NavItem(cfg);
6267         this.register(cn);
6268         cn.parentId = this.id;
6269         cn.onRender(this.el, null);
6270         return cn;
6271     },
6272     /**
6273     * register a Navigation item
6274     * @param {Roo.bootstrap.NavItem} the navitem to add
6275     */
6276     register : function(item)
6277     {
6278         this.navItems.push( item);
6279         item.navId = this.navId;
6280     
6281     },
6282     
6283     /**
6284     * clear all the Navigation item
6285     */
6286    
6287     clearAll : function()
6288     {
6289         this.navItems = [];
6290         this.el.dom.innerHTML = '';
6291     },
6292     
6293     getNavItem: function(tabId)
6294     {
6295         var ret = false;
6296         Roo.each(this.navItems, function(e) {
6297             if (e.tabId == tabId) {
6298                ret =  e;
6299                return false;
6300             }
6301             return true;
6302             
6303         });
6304         return ret;
6305     },
6306     
6307     setActiveNext : function()
6308     {
6309         var i = this.indexOfNav(this.getActive());
6310         if (i > this.navItems.length) {
6311             return;
6312         }
6313         this.setActiveItem(this.navItems[i+1]);
6314     },
6315     setActivePrev : function()
6316     {
6317         var i = this.indexOfNav(this.getActive());
6318         if (i  < 1) {
6319             return;
6320         }
6321         this.setActiveItem(this.navItems[i-1]);
6322     },
6323     clearWasActive : function(except) {
6324         Roo.each(this.navItems, function(e) {
6325             if (e.tabId != except.tabId && e.was_active) {
6326                e.was_active = false;
6327                return false;
6328             }
6329             return true;
6330             
6331         });
6332     },
6333     getWasActive : function ()
6334     {
6335         var r = false;
6336         Roo.each(this.navItems, function(e) {
6337             if (e.was_active) {
6338                r = e;
6339                return false;
6340             }
6341             return true;
6342             
6343         });
6344         return r;
6345     }
6346     
6347     
6348 });
6349
6350  
6351 Roo.apply(Roo.bootstrap.NavGroup, {
6352     
6353     groups: {},
6354      /**
6355     * register a Navigation Group
6356     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6357     */
6358     register : function(navgrp)
6359     {
6360         this.groups[navgrp.navId] = navgrp;
6361         
6362     },
6363     /**
6364     * fetch a Navigation Group based on the navigation ID
6365     * @param {string} the navgroup to add
6366     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6367     */
6368     get: function(navId) {
6369         if (typeof(this.groups[navId]) == 'undefined') {
6370             return false;
6371             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6372         }
6373         return this.groups[navId] ;
6374     }
6375     
6376     
6377     
6378 });
6379
6380  /**
6381  * @class Roo.bootstrap.NavItem
6382  * @extends Roo.bootstrap.Component
6383  * @licence LGPL
6384  * Bootstrap Navbar.NavItem class
6385  * 
6386  * @cfg {String} href  link to
6387  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6388  * @cfg {Boolean} button_outline show and outlined button
6389  * @cfg {String} html content of button
6390  * @cfg {String} badge text inside badge
6391  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6392  * @cfg {String} glyphicon DEPRICATED - use fa
6393  * @cfg {String} icon DEPRICATED - use fa
6394  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6395  * @cfg {Boolean} active Is item active
6396  * @cfg {Boolean} disabled Is item disabled
6397  * @cfg {String} linkcls  Link Class
6398  * @cfg {Boolean} preventDefault (true | false) default false
6399  * @cfg {String} tabId the tab that this item activates.
6400  * @cfg {String} tagtype (a|span) render as a href or span?
6401  * @cfg {Boolean} animateRef (true|false) link to element default false  
6402  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6403   
6404  * @constructor
6405  * Create a new Navbar Item
6406  * @param {Object} config The config object
6407  */
6408 Roo.bootstrap.NavItem = function(config){
6409     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6410     this.addEvents({
6411         // raw events
6412         /**
6413          * @event click
6414          * The raw click event for the entire grid.
6415          * @param {Roo.EventObject} e
6416          */
6417         "click" : true,
6418          /**
6419             * @event changed
6420             * Fires when the active item active state changes
6421             * @param {Roo.bootstrap.NavItem} this
6422             * @param {boolean} state the new state
6423              
6424          */
6425         'changed': true,
6426         /**
6427             * @event scrollto
6428             * Fires when scroll to element
6429             * @param {Roo.bootstrap.NavItem} this
6430             * @param {Object} options
6431             * @param {Roo.EventObject} e
6432              
6433          */
6434         'scrollto': true
6435     });
6436    
6437 };
6438
6439 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6440     
6441     href: false,
6442     html: '',
6443     badge: '',
6444     icon: false,
6445     fa : false,
6446     glyphicon: false,
6447     active: false,
6448     preventDefault : false,
6449     tabId : false,
6450     tagtype : 'a',
6451     tag: 'li',
6452     disabled : false,
6453     animateRef : false,
6454     was_active : false,
6455     button_weight : '',
6456     button_outline : false,
6457     linkcls : '',
6458     navLink: false,
6459     
6460     getAutoCreate : function(){
6461          
6462         var cfg = {
6463             tag: this.tag,
6464             cls: 'nav-item'
6465         };
6466         
6467         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6468         
6469         if (this.active) {
6470             cfg.cls +=  ' active' ;
6471         }
6472         if (this.disabled) {
6473             cfg.cls += ' disabled';
6474         }
6475         
6476         // BS4 only?
6477         if (this.button_weight.length) {
6478             cfg.tag = this.href ? 'a' : 'button';
6479             cfg.html = this.html || '';
6480             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6481             if (this.href) {
6482                 cfg.href = this.href;
6483             }
6484             if (this.fa) {
6485                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6486             } else {
6487                 cfg.cls += " nav-html";
6488             }
6489             
6490             // menu .. should add dropdown-menu class - so no need for carat..
6491             
6492             if (this.badge !== '') {
6493                  
6494                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6495             }
6496             return cfg;
6497         }
6498         
6499         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6500             cfg.cn = [
6501                 {
6502                     tag: this.tagtype,
6503                     href : this.href || "#",
6504                     html: this.html || '',
6505                     cls : ''
6506                 }
6507             ];
6508             if (this.tagtype == 'a') {
6509                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6510         
6511             }
6512             if (this.icon) {
6513                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6514             } else  if (this.fa) {
6515                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6516             } else if(this.glyphicon) {
6517                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6518             } else {
6519                 cfg.cn[0].cls += " nav-html";
6520             }
6521             
6522             if (this.menu) {
6523                 cfg.cn[0].html += " <span class='caret'></span>";
6524              
6525             }
6526             
6527             if (this.badge !== '') {
6528                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6529             }
6530         }
6531         
6532         
6533         
6534         return cfg;
6535     },
6536     onRender : function(ct, position)
6537     {
6538        // Roo.log("Call onRender: " + this.xtype);
6539         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6540             this.tag = 'div';
6541         }
6542         
6543         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6544         this.navLink = this.el.select('.nav-link',true).first();
6545         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6546         return ret;
6547     },
6548       
6549     
6550     initEvents: function() 
6551     {
6552         if (typeof (this.menu) != 'undefined') {
6553             this.menu.parentType = this.xtype;
6554             this.menu.triggerEl = this.el;
6555             this.menu = this.addxtype(Roo.apply({}, this.menu));
6556         }
6557         
6558         this.el.on('click', this.onClick, this);
6559         
6560         //if(this.tagtype == 'span'){
6561         //    this.el.select('span',true).on('click', this.onClick, this);
6562         //}
6563        
6564         // at this point parent should be available..
6565         this.parent().register(this);
6566     },
6567     
6568     onClick : function(e)
6569     {
6570         if (e.getTarget('.dropdown-menu-item')) {
6571             // did you click on a menu itemm.... - then don't trigger onclick..
6572             return;
6573         }
6574         
6575         if(
6576                 this.preventDefault || 
6577                 this.href == '#' 
6578         ){
6579             Roo.log("NavItem - prevent Default?");
6580             e.preventDefault();
6581         }
6582         
6583         if (this.disabled) {
6584             return;
6585         }
6586         
6587         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6588         if (tg && tg.transition) {
6589             Roo.log("waiting for the transitionend");
6590             return;
6591         }
6592         
6593         
6594         
6595         //Roo.log("fire event clicked");
6596         if(this.fireEvent('click', this, e) === false){
6597             return;
6598         };
6599         
6600         if(this.tagtype == 'span'){
6601             return;
6602         }
6603         
6604         //Roo.log(this.href);
6605         var ael = this.el.select('a',true).first();
6606         //Roo.log(ael);
6607         
6608         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6609             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6610             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6611                 return; // ignore... - it's a 'hash' to another page.
6612             }
6613             Roo.log("NavItem - prevent Default?");
6614             e.preventDefault();
6615             this.scrollToElement(e);
6616         }
6617         
6618         
6619         var p =  this.parent();
6620    
6621         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6622             if (typeof(p.setActiveItem) !== 'undefined') {
6623                 p.setActiveItem(this);
6624             }
6625         }
6626         
6627         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6628         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6629             // remove the collapsed menu expand...
6630             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6631         }
6632     },
6633     
6634     isActive: function () {
6635         return this.active
6636     },
6637     setActive : function(state, fire, is_was_active)
6638     {
6639         if (this.active && !state && this.navId) {
6640             this.was_active = true;
6641             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6642             if (nv) {
6643                 nv.clearWasActive(this);
6644             }
6645             
6646         }
6647         this.active = state;
6648         
6649         if (!state ) {
6650             this.el.removeClass('active');
6651             this.navLink ? this.navLink.removeClass('active') : false;
6652         } else if (!this.el.hasClass('active')) {
6653             
6654             this.el.addClass('active');
6655             if (Roo.bootstrap.version == 4 && this.navLink ) {
6656                 this.navLink.addClass('active');
6657             }
6658             
6659         }
6660         if (fire) {
6661             this.fireEvent('changed', this, state);
6662         }
6663         
6664         // show a panel if it's registered and related..
6665         
6666         if (!this.navId || !this.tabId || !state || is_was_active) {
6667             return;
6668         }
6669         
6670         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6671         if (!tg) {
6672             return;
6673         }
6674         var pan = tg.getPanelByName(this.tabId);
6675         if (!pan) {
6676             return;
6677         }
6678         // if we can not flip to new panel - go back to old nav highlight..
6679         if (false == tg.showPanel(pan)) {
6680             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6681             if (nv) {
6682                 var onav = nv.getWasActive();
6683                 if (onav) {
6684                     onav.setActive(true, false, true);
6685                 }
6686             }
6687             
6688         }
6689         
6690         
6691         
6692     },
6693      // this should not be here...
6694     setDisabled : function(state)
6695     {
6696         this.disabled = state;
6697         if (!state ) {
6698             this.el.removeClass('disabled');
6699         } else if (!this.el.hasClass('disabled')) {
6700             this.el.addClass('disabled');
6701         }
6702         
6703     },
6704     
6705     /**
6706      * Fetch the element to display the tooltip on.
6707      * @return {Roo.Element} defaults to this.el
6708      */
6709     tooltipEl : function()
6710     {
6711         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6712     },
6713     
6714     scrollToElement : function(e)
6715     {
6716         var c = document.body;
6717         
6718         /*
6719          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6720          */
6721         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6722             c = document.documentElement;
6723         }
6724         
6725         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6726         
6727         if(!target){
6728             return;
6729         }
6730
6731         var o = target.calcOffsetsTo(c);
6732         
6733         var options = {
6734             target : target,
6735             value : o[1]
6736         };
6737         
6738         this.fireEvent('scrollto', this, options, e);
6739         
6740         Roo.get(c).scrollTo('top', options.value, true);
6741         
6742         return;
6743     },
6744     /**
6745      * Set the HTML (text content) of the item
6746      * @param {string} html  content for the nav item
6747      */
6748     setHtml : function(html)
6749     {
6750         this.html = html;
6751         this.htmlEl.dom.innerHTML = html;
6752         
6753     } 
6754 });
6755  
6756
6757  /*
6758  * - LGPL
6759  *
6760  * sidebar item
6761  *
6762  *  li
6763  *    <span> icon </span>
6764  *    <span> text </span>
6765  *    <span>badge </span>
6766  */
6767
6768 /**
6769  * @class Roo.bootstrap.NavSidebarItem
6770  * @extends Roo.bootstrap.NavItem
6771  * Bootstrap Navbar.NavSidebarItem class
6772  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6773  * {Boolean} open is the menu open
6774  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6775  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6776  * {String} buttonSize (sm|md|lg)the extra classes for the button
6777  * {Boolean} showArrow show arrow next to the text (default true)
6778  * @constructor
6779  * Create a new Navbar Button
6780  * @param {Object} config The config object
6781  */
6782 Roo.bootstrap.NavSidebarItem = function(config){
6783     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6784     this.addEvents({
6785         // raw events
6786         /**
6787          * @event click
6788          * The raw click event for the entire grid.
6789          * @param {Roo.EventObject} e
6790          */
6791         "click" : true,
6792          /**
6793             * @event changed
6794             * Fires when the active item active state changes
6795             * @param {Roo.bootstrap.NavSidebarItem} this
6796             * @param {boolean} state the new state
6797              
6798          */
6799         'changed': true
6800     });
6801    
6802 };
6803
6804 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6805     
6806     badgeWeight : 'default',
6807     
6808     open: false,
6809     
6810     buttonView : false,
6811     
6812     buttonWeight : 'default',
6813     
6814     buttonSize : 'md',
6815     
6816     showArrow : true,
6817     
6818     getAutoCreate : function(){
6819         
6820         
6821         var a = {
6822                 tag: 'a',
6823                 href : this.href || '#',
6824                 cls: '',
6825                 html : '',
6826                 cn : []
6827         };
6828         
6829         if(this.buttonView){
6830             a = {
6831                 tag: 'button',
6832                 href : this.href || '#',
6833                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6834                 html : this.html,
6835                 cn : []
6836             };
6837         }
6838         
6839         var cfg = {
6840             tag: 'li',
6841             cls: '',
6842             cn: [ a ]
6843         };
6844         
6845         if (this.active) {
6846             cfg.cls += ' active';
6847         }
6848         
6849         if (this.disabled) {
6850             cfg.cls += ' disabled';
6851         }
6852         if (this.open) {
6853             cfg.cls += ' open x-open';
6854         }
6855         // left icon..
6856         if (this.glyphicon || this.icon) {
6857             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6858             a.cn.push({ tag : 'i', cls : c }) ;
6859         }
6860         
6861         if(!this.buttonView){
6862             var span = {
6863                 tag: 'span',
6864                 html : this.html || ''
6865             };
6866
6867             a.cn.push(span);
6868             
6869         }
6870         
6871         if (this.badge !== '') {
6872             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6873         }
6874         
6875         if (this.menu) {
6876             
6877             if(this.showArrow){
6878                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6879             }
6880             
6881             a.cls += ' dropdown-toggle treeview' ;
6882         }
6883         
6884         return cfg;
6885     },
6886     
6887     initEvents : function()
6888     { 
6889         if (typeof (this.menu) != 'undefined') {
6890             this.menu.parentType = this.xtype;
6891             this.menu.triggerEl = this.el;
6892             this.menu = this.addxtype(Roo.apply({}, this.menu));
6893         }
6894         
6895         this.el.on('click', this.onClick, this);
6896         
6897         if(this.badge !== ''){
6898             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6899         }
6900         
6901     },
6902     
6903     onClick : function(e)
6904     {
6905         if(this.disabled){
6906             e.preventDefault();
6907             return;
6908         }
6909         
6910         if(this.preventDefault){
6911             e.preventDefault();
6912         }
6913         
6914         this.fireEvent('click', this, e);
6915     },
6916     
6917     disable : function()
6918     {
6919         this.setDisabled(true);
6920     },
6921     
6922     enable : function()
6923     {
6924         this.setDisabled(false);
6925     },
6926     
6927     setDisabled : function(state)
6928     {
6929         if(this.disabled == state){
6930             return;
6931         }
6932         
6933         this.disabled = state;
6934         
6935         if (state) {
6936             this.el.addClass('disabled');
6937             return;
6938         }
6939         
6940         this.el.removeClass('disabled');
6941         
6942         return;
6943     },
6944     
6945     setActive : function(state)
6946     {
6947         if(this.active == state){
6948             return;
6949         }
6950         
6951         this.active = state;
6952         
6953         if (state) {
6954             this.el.addClass('active');
6955             return;
6956         }
6957         
6958         this.el.removeClass('active');
6959         
6960         return;
6961     },
6962     
6963     isActive: function () 
6964     {
6965         return this.active;
6966     },
6967     
6968     setBadge : function(str)
6969     {
6970         if(!this.badgeEl){
6971             return;
6972         }
6973         
6974         this.badgeEl.dom.innerHTML = str;
6975     }
6976     
6977    
6978      
6979  
6980 });
6981  
6982
6983  /*
6984  * - LGPL
6985  *
6986  *  Breadcrumb Nav
6987  * 
6988  */
6989 Roo.namespace('Roo.bootstrap.breadcrumb');
6990
6991
6992 /**
6993  * @class Roo.bootstrap.breadcrumb.Nav
6994  * @extends Roo.bootstrap.Component
6995  * Bootstrap Breadcrumb Nav Class
6996  *  
6997  * @children Roo.bootstrap.breadcrumb.Item
6998  * 
6999  * @constructor
7000  * Create a new breadcrumb.Nav
7001  * @param {Object} config The config object
7002  */
7003
7004
7005 Roo.bootstrap.breadcrumb.Nav = function(config){
7006     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7007     
7008     
7009 };
7010
7011 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7012     
7013     getAutoCreate : function()
7014     {
7015
7016         var cfg = {
7017             tag: 'nav',
7018             cn : [
7019                 {
7020                     tag : 'ol',
7021                     cls : 'breadcrumb'
7022                 }
7023             ]
7024             
7025         };
7026           
7027         return cfg;
7028     },
7029     
7030     initEvents: function()
7031     {
7032         this.olEl = this.el.select('ol',true).first();    
7033     },
7034     getChildContainer : function()
7035     {
7036         return this.olEl;  
7037     }
7038     
7039 });
7040
7041  /*
7042  * - LGPL
7043  *
7044  *  Breadcrumb Item
7045  * 
7046  */
7047
7048
7049 /**
7050  * @class Roo.bootstrap.breadcrumb.Nav
7051  * @extends Roo.bootstrap.Component
7052  * Bootstrap Breadcrumb Nav Class
7053  *  
7054  * @children Roo.bootstrap.breadcrumb.Component
7055  * @cfg {String} html the content of the link.
7056  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7057  * @cfg {Boolean} active is it active
7058
7059  * 
7060  * @constructor
7061  * Create a new breadcrumb.Nav
7062  * @param {Object} config The config object
7063  */
7064
7065 Roo.bootstrap.breadcrumb.Item = function(config){
7066     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7067     this.addEvents({
7068         // img events
7069         /**
7070          * @event click
7071          * The img click event for the img.
7072          * @param {Roo.EventObject} e
7073          */
7074         "click" : true
7075     });
7076     
7077 };
7078
7079 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7080     
7081     href: false,
7082     html : '',
7083     
7084     getAutoCreate : function()
7085     {
7086
7087         var cfg = {
7088             tag: 'li',
7089             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7090         };
7091         if (this.href !== false) {
7092             cfg.cn = [{
7093                 tag : 'a',
7094                 href : this.href,
7095                 html : this.html
7096             }];
7097         } else {
7098             cfg.html = this.html;
7099         }
7100         
7101         return cfg;
7102     },
7103     
7104     initEvents: function()
7105     {
7106         if (this.href) {
7107             this.el.select('a', true).first().on('click',this.onClick, this)
7108         }
7109         
7110     },
7111     onClick : function(e)
7112     {
7113         e.preventDefault();
7114         this.fireEvent('click',this,  e);
7115     }
7116     
7117 });
7118
7119  /*
7120  * - LGPL
7121  *
7122  * row
7123  * 
7124  */
7125
7126 /**
7127  * @class Roo.bootstrap.Row
7128  * @extends Roo.bootstrap.Component
7129  * @children Roo.bootstrap.Component
7130  * Bootstrap Row class (contains columns...)
7131  * 
7132  * @constructor
7133  * Create a new Row
7134  * @param {Object} config The config object
7135  */
7136
7137 Roo.bootstrap.Row = function(config){
7138     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7139 };
7140
7141 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7142     
7143     getAutoCreate : function(){
7144        return {
7145             cls: 'row clearfix'
7146        };
7147     }
7148     
7149     
7150 });
7151
7152  
7153
7154  /*
7155  * - LGPL
7156  *
7157  * pagination
7158  * 
7159  */
7160
7161 /**
7162  * @class Roo.bootstrap.Pagination
7163  * @extends Roo.bootstrap.Component
7164  * Bootstrap Pagination class
7165  * @cfg {String} size xs | sm | md | lg
7166  * @cfg {Boolean} inverse false | true
7167  * 
7168  * @constructor
7169  * Create a new Pagination
7170  * @param {Object} config The config object
7171  */
7172
7173 Roo.bootstrap.Pagination = function(config){
7174     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7175 };
7176
7177 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7178     
7179     cls: false,
7180     size: false,
7181     inverse: false,
7182     
7183     getAutoCreate : function(){
7184         var cfg = {
7185             tag: 'ul',
7186                 cls: 'pagination'
7187         };
7188         if (this.inverse) {
7189             cfg.cls += ' inverse';
7190         }
7191         if (this.html) {
7192             cfg.html=this.html;
7193         }
7194         if (this.cls) {
7195             cfg.cls += " " + this.cls;
7196         }
7197         return cfg;
7198     }
7199    
7200 });
7201
7202  
7203
7204  /*
7205  * - LGPL
7206  *
7207  * Pagination item
7208  * 
7209  */
7210
7211
7212 /**
7213  * @class Roo.bootstrap.PaginationItem
7214  * @extends Roo.bootstrap.Component
7215  * Bootstrap PaginationItem class
7216  * @cfg {String} html text
7217  * @cfg {String} href the link
7218  * @cfg {Boolean} preventDefault (true | false) default true
7219  * @cfg {Boolean} active (true | false) default false
7220  * @cfg {Boolean} disabled default false
7221  * 
7222  * 
7223  * @constructor
7224  * Create a new PaginationItem
7225  * @param {Object} config The config object
7226  */
7227
7228
7229 Roo.bootstrap.PaginationItem = function(config){
7230     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7231     this.addEvents({
7232         // raw events
7233         /**
7234          * @event click
7235          * The raw click event for the entire grid.
7236          * @param {Roo.EventObject} e
7237          */
7238         "click" : true
7239     });
7240 };
7241
7242 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7243     
7244     href : false,
7245     html : false,
7246     preventDefault: true,
7247     active : false,
7248     cls : false,
7249     disabled: false,
7250     
7251     getAutoCreate : function(){
7252         var cfg= {
7253             tag: 'li',
7254             cn: [
7255                 {
7256                     tag : 'a',
7257                     href : this.href ? this.href : '#',
7258                     html : this.html ? this.html : ''
7259                 }
7260             ]
7261         };
7262         
7263         if(this.cls){
7264             cfg.cls = this.cls;
7265         }
7266         
7267         if(this.disabled){
7268             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7269         }
7270         
7271         if(this.active){
7272             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7273         }
7274         
7275         return cfg;
7276     },
7277     
7278     initEvents: function() {
7279         
7280         this.el.on('click', this.onClick, this);
7281         
7282     },
7283     onClick : function(e)
7284     {
7285         Roo.log('PaginationItem on click ');
7286         if(this.preventDefault){
7287             e.preventDefault();
7288         }
7289         
7290         if(this.disabled){
7291             return;
7292         }
7293         
7294         this.fireEvent('click', this, e);
7295     }
7296    
7297 });
7298
7299  
7300
7301  /*
7302  * - LGPL
7303  *
7304  * slider
7305  * 
7306  */
7307
7308
7309 /**
7310  * @class Roo.bootstrap.Slider
7311  * @extends Roo.bootstrap.Component
7312  * Bootstrap Slider class
7313  *    
7314  * @constructor
7315  * Create a new Slider
7316  * @param {Object} config The config object
7317  */
7318
7319 Roo.bootstrap.Slider = function(config){
7320     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7321 };
7322
7323 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7324     
7325     getAutoCreate : function(){
7326         
7327         var cfg = {
7328             tag: 'div',
7329             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7330             cn: [
7331                 {
7332                     tag: 'a',
7333                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7334                 }
7335             ]
7336         };
7337         
7338         return cfg;
7339     }
7340    
7341 });
7342
7343  /*
7344  * Based on:
7345  * Ext JS Library 1.1.1
7346  * Copyright(c) 2006-2007, Ext JS, LLC.
7347  *
7348  * Originally Released Under LGPL - original licence link has changed is not relivant.
7349  *
7350  * Fork - LGPL
7351  * <script type="text/javascript">
7352  */
7353  /**
7354  * @extends Roo.dd.DDProxy
7355  * @class Roo.grid.SplitDragZone
7356  * Support for Column Header resizing
7357  * @constructor
7358  * @param {Object} config
7359  */
7360 // private
7361 // This is a support class used internally by the Grid components
7362 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7363     this.grid = grid;
7364     this.view = grid.getView();
7365     this.proxy = this.view.resizeProxy;
7366     Roo.grid.SplitDragZone.superclass.constructor.call(
7367         this,
7368         hd, // ID
7369         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7370         {  // CONFIG
7371             dragElId : Roo.id(this.proxy.dom),
7372             resizeFrame:false
7373         }
7374     );
7375     
7376     this.setHandleElId(Roo.id(hd));
7377     if (hd2 !== false) {
7378         this.setOuterHandleElId(Roo.id(hd2));
7379     }
7380     
7381     this.scroll = false;
7382 };
7383 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7384     fly: Roo.Element.fly,
7385
7386     b4StartDrag : function(x, y){
7387         this.view.headersDisabled = true;
7388         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7389                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7390         );
7391         this.proxy.setHeight(h);
7392         
7393         // for old system colWidth really stored the actual width?
7394         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7395         // which in reality did not work.. - it worked only for fixed sizes
7396         // for resizable we need to use actual sizes.
7397         var w = this.cm.getColumnWidth(this.cellIndex);
7398         if (!this.view.mainWrap) {
7399             // bootstrap.
7400             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7401         }
7402         
7403         
7404         
7405         // this was w-this.grid.minColumnWidth;
7406         // doesnt really make sense? - w = thie curren width or the rendered one?
7407         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7408         this.resetConstraints();
7409         this.setXConstraint(minw, 1000);
7410         this.setYConstraint(0, 0);
7411         this.minX = x - minw;
7412         this.maxX = x + 1000;
7413         this.startPos = x;
7414         if (!this.view.mainWrap) { // this is Bootstrap code..
7415             this.getDragEl().style.display='block';
7416         }
7417         
7418         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7419     },
7420
7421
7422     handleMouseDown : function(e){
7423         ev = Roo.EventObject.setEvent(e);
7424         var t = this.fly(ev.getTarget());
7425         if(t.hasClass("x-grid-split")){
7426             this.cellIndex = this.view.getCellIndex(t.dom);
7427             this.split = t.dom;
7428             this.cm = this.grid.colModel;
7429             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7430                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7431             }
7432         }
7433     },
7434
7435     endDrag : function(e){
7436         this.view.headersDisabled = false;
7437         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7438         var diff = endX - this.startPos;
7439         // 
7440         var w = this.cm.getColumnWidth(this.cellIndex);
7441         if (!this.view.mainWrap) {
7442             w = 0;
7443         }
7444         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7445     },
7446
7447     autoOffset : function(){
7448         this.setDelta(0,0);
7449     }
7450 });/*
7451  * Based on:
7452  * Ext JS Library 1.1.1
7453  * Copyright(c) 2006-2007, Ext JS, LLC.
7454  *
7455  * Originally Released Under LGPL - original licence link has changed is not relivant.
7456  *
7457  * Fork - LGPL
7458  * <script type="text/javascript">
7459  */
7460
7461 /**
7462  * @class Roo.grid.AbstractSelectionModel
7463  * @extends Roo.util.Observable
7464  * @abstract
7465  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7466  * implemented by descendant classes.  This class should not be directly instantiated.
7467  * @constructor
7468  */
7469 Roo.grid.AbstractSelectionModel = function(){
7470     this.locked = false;
7471     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7472 };
7473
7474 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7475     /** @ignore Called by the grid automatically. Do not call directly. */
7476     init : function(grid){
7477         this.grid = grid;
7478         this.initEvents();
7479     },
7480
7481     /**
7482      * Locks the selections.
7483      */
7484     lock : function(){
7485         this.locked = true;
7486     },
7487
7488     /**
7489      * Unlocks the selections.
7490      */
7491     unlock : function(){
7492         this.locked = false;
7493     },
7494
7495     /**
7496      * Returns true if the selections are locked.
7497      * @return {Boolean}
7498      */
7499     isLocked : function(){
7500         return this.locked;
7501     }
7502 });/*
7503  * Based on:
7504  * Ext JS Library 1.1.1
7505  * Copyright(c) 2006-2007, Ext JS, LLC.
7506  *
7507  * Originally Released Under LGPL - original licence link has changed is not relivant.
7508  *
7509  * Fork - LGPL
7510  * <script type="text/javascript">
7511  */
7512 /**
7513  * @extends Roo.grid.AbstractSelectionModel
7514  * @class Roo.grid.RowSelectionModel
7515  * The default SelectionModel used by {@link Roo.grid.Grid}.
7516  * It supports multiple selections and keyboard selection/navigation. 
7517  * @constructor
7518  * @param {Object} config
7519  */
7520 Roo.grid.RowSelectionModel = function(config){
7521     Roo.apply(this, config);
7522     this.selections = new Roo.util.MixedCollection(false, function(o){
7523         return o.id;
7524     });
7525
7526     this.last = false;
7527     this.lastActive = false;
7528
7529     this.addEvents({
7530         /**
7531         * @event selectionchange
7532         * Fires when the selection changes
7533         * @param {SelectionModel} this
7534         */
7535        "selectionchange" : true,
7536        /**
7537         * @event afterselectionchange
7538         * Fires after the selection changes (eg. by key press or clicking)
7539         * @param {SelectionModel} this
7540         */
7541        "afterselectionchange" : true,
7542        /**
7543         * @event beforerowselect
7544         * Fires when a row is selected being selected, return false to cancel.
7545         * @param {SelectionModel} this
7546         * @param {Number} rowIndex The selected index
7547         * @param {Boolean} keepExisting False if other selections will be cleared
7548         */
7549        "beforerowselect" : true,
7550        /**
7551         * @event rowselect
7552         * Fires when a row is selected.
7553         * @param {SelectionModel} this
7554         * @param {Number} rowIndex The selected index
7555         * @param {Roo.data.Record} r The record
7556         */
7557        "rowselect" : true,
7558        /**
7559         * @event rowdeselect
7560         * Fires when a row is deselected.
7561         * @param {SelectionModel} this
7562         * @param {Number} rowIndex The selected index
7563         */
7564         "rowdeselect" : true
7565     });
7566     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7567     this.locked = false;
7568 };
7569
7570 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7571     /**
7572      * @cfg {Boolean} singleSelect
7573      * True to allow selection of only one row at a time (defaults to false)
7574      */
7575     singleSelect : false,
7576
7577     // private
7578     initEvents : function(){
7579
7580         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7581             this.grid.on("mousedown", this.handleMouseDown, this);
7582         }else{ // allow click to work like normal
7583             this.grid.on("rowclick", this.handleDragableRowClick, this);
7584         }
7585         // bootstrap does not have a view..
7586         var view = this.grid.view ? this.grid.view : this.grid;
7587         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7588             "up" : function(e){
7589                 if(!e.shiftKey){
7590                     this.selectPrevious(e.shiftKey);
7591                 }else if(this.last !== false && this.lastActive !== false){
7592                     var last = this.last;
7593                     this.selectRange(this.last,  this.lastActive-1);
7594                     view.focusRow(this.lastActive);
7595                     if(last !== false){
7596                         this.last = last;
7597                     }
7598                 }else{
7599                     this.selectFirstRow();
7600                 }
7601                 this.fireEvent("afterselectionchange", this);
7602             },
7603             "down" : function(e){
7604                 if(!e.shiftKey){
7605                     this.selectNext(e.shiftKey);
7606                 }else if(this.last !== false && this.lastActive !== false){
7607                     var last = this.last;
7608                     this.selectRange(this.last,  this.lastActive+1);
7609                     view.focusRow(this.lastActive);
7610                     if(last !== false){
7611                         this.last = last;
7612                     }
7613                 }else{
7614                     this.selectFirstRow();
7615                 }
7616                 this.fireEvent("afterselectionchange", this);
7617             },
7618             scope: this
7619         });
7620
7621          
7622         view.on("refresh", this.onRefresh, this);
7623         view.on("rowupdated", this.onRowUpdated, this);
7624         view.on("rowremoved", this.onRemove, this);
7625     },
7626
7627     // private
7628     onRefresh : function(){
7629         var ds = this.grid.ds, i, v = this.grid.view;
7630         var s = this.selections;
7631         s.each(function(r){
7632             if((i = ds.indexOfId(r.id)) != -1){
7633                 v.onRowSelect(i);
7634                 s.add(ds.getAt(i)); // updating the selection relate data
7635             }else{
7636                 s.remove(r);
7637             }
7638         });
7639     },
7640
7641     // private
7642     onRemove : function(v, index, r){
7643         this.selections.remove(r);
7644     },
7645
7646     // private
7647     onRowUpdated : function(v, index, r){
7648         if(this.isSelected(r)){
7649             v.onRowSelect(index);
7650         }
7651     },
7652
7653     /**
7654      * Select records.
7655      * @param {Array} records The records to select
7656      * @param {Boolean} keepExisting (optional) True to keep existing selections
7657      */
7658     selectRecords : function(records, keepExisting){
7659         if(!keepExisting){
7660             this.clearSelections();
7661         }
7662         var ds = this.grid.ds;
7663         for(var i = 0, len = records.length; i < len; i++){
7664             this.selectRow(ds.indexOf(records[i]), true);
7665         }
7666     },
7667
7668     /**
7669      * Gets the number of selected rows.
7670      * @return {Number}
7671      */
7672     getCount : function(){
7673         return this.selections.length;
7674     },
7675
7676     /**
7677      * Selects the first row in the grid.
7678      */
7679     selectFirstRow : function(){
7680         this.selectRow(0);
7681     },
7682
7683     /**
7684      * Select the last row.
7685      * @param {Boolean} keepExisting (optional) True to keep existing selections
7686      */
7687     selectLastRow : function(keepExisting){
7688         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7689     },
7690
7691     /**
7692      * Selects the row immediately following the last selected row.
7693      * @param {Boolean} keepExisting (optional) True to keep existing selections
7694      */
7695     selectNext : function(keepExisting){
7696         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7697             this.selectRow(this.last+1, keepExisting);
7698             var view = this.grid.view ? this.grid.view : this.grid;
7699             view.focusRow(this.last);
7700         }
7701     },
7702
7703     /**
7704      * Selects the row that precedes the last selected row.
7705      * @param {Boolean} keepExisting (optional) True to keep existing selections
7706      */
7707     selectPrevious : function(keepExisting){
7708         if(this.last){
7709             this.selectRow(this.last-1, keepExisting);
7710             var view = this.grid.view ? this.grid.view : this.grid;
7711             view.focusRow(this.last);
7712         }
7713     },
7714
7715     /**
7716      * Returns the selected records
7717      * @return {Array} Array of selected records
7718      */
7719     getSelections : function(){
7720         return [].concat(this.selections.items);
7721     },
7722
7723     /**
7724      * Returns the first selected record.
7725      * @return {Record}
7726      */
7727     getSelected : function(){
7728         return this.selections.itemAt(0);
7729     },
7730
7731
7732     /**
7733      * Clears all selections.
7734      */
7735     clearSelections : function(fast){
7736         if(this.locked) {
7737             return;
7738         }
7739         if(fast !== true){
7740             var ds = this.grid.ds;
7741             var s = this.selections;
7742             s.each(function(r){
7743                 this.deselectRow(ds.indexOfId(r.id));
7744             }, this);
7745             s.clear();
7746         }else{
7747             this.selections.clear();
7748         }
7749         this.last = false;
7750     },
7751
7752
7753     /**
7754      * Selects all rows.
7755      */
7756     selectAll : function(){
7757         if(this.locked) {
7758             return;
7759         }
7760         this.selections.clear();
7761         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7762             this.selectRow(i, true);
7763         }
7764     },
7765
7766     /**
7767      * Returns True if there is a selection.
7768      * @return {Boolean}
7769      */
7770     hasSelection : function(){
7771         return this.selections.length > 0;
7772     },
7773
7774     /**
7775      * Returns True if the specified row is selected.
7776      * @param {Number/Record} record The record or index of the record to check
7777      * @return {Boolean}
7778      */
7779     isSelected : function(index){
7780         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7781         return (r && this.selections.key(r.id) ? true : false);
7782     },
7783
7784     /**
7785      * Returns True if the specified record id is selected.
7786      * @param {String} id The id of record to check
7787      * @return {Boolean}
7788      */
7789     isIdSelected : function(id){
7790         return (this.selections.key(id) ? true : false);
7791     },
7792
7793     // private
7794     handleMouseDown : function(e, t)
7795     {
7796         var view = this.grid.view ? this.grid.view : this.grid;
7797         var rowIndex;
7798         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7799             return;
7800         };
7801         if(e.shiftKey && this.last !== false){
7802             var last = this.last;
7803             this.selectRange(last, rowIndex, e.ctrlKey);
7804             this.last = last; // reset the last
7805             view.focusRow(rowIndex);
7806         }else{
7807             var isSelected = this.isSelected(rowIndex);
7808             if(e.button !== 0 && isSelected){
7809                 view.focusRow(rowIndex);
7810             }else if(e.ctrlKey && isSelected){
7811                 this.deselectRow(rowIndex);
7812             }else if(!isSelected){
7813                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7814                 view.focusRow(rowIndex);
7815             }
7816         }
7817         this.fireEvent("afterselectionchange", this);
7818     },
7819     // private
7820     handleDragableRowClick :  function(grid, rowIndex, e) 
7821     {
7822         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7823             this.selectRow(rowIndex, false);
7824             var view = this.grid.view ? this.grid.view : this.grid;
7825             view.focusRow(rowIndex);
7826              this.fireEvent("afterselectionchange", this);
7827         }
7828     },
7829     
7830     /**
7831      * Selects multiple rows.
7832      * @param {Array} rows Array of the indexes of the row to select
7833      * @param {Boolean} keepExisting (optional) True to keep existing selections
7834      */
7835     selectRows : function(rows, keepExisting){
7836         if(!keepExisting){
7837             this.clearSelections();
7838         }
7839         for(var i = 0, len = rows.length; i < len; i++){
7840             this.selectRow(rows[i], true);
7841         }
7842     },
7843
7844     /**
7845      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7846      * @param {Number} startRow The index of the first row in the range
7847      * @param {Number} endRow The index of the last row in the range
7848      * @param {Boolean} keepExisting (optional) True to retain existing selections
7849      */
7850     selectRange : function(startRow, endRow, keepExisting){
7851         if(this.locked) {
7852             return;
7853         }
7854         if(!keepExisting){
7855             this.clearSelections();
7856         }
7857         if(startRow <= endRow){
7858             for(var i = startRow; i <= endRow; i++){
7859                 this.selectRow(i, true);
7860             }
7861         }else{
7862             for(var i = startRow; i >= endRow; i--){
7863                 this.selectRow(i, true);
7864             }
7865         }
7866     },
7867
7868     /**
7869      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7870      * @param {Number} startRow The index of the first row in the range
7871      * @param {Number} endRow The index of the last row in the range
7872      */
7873     deselectRange : function(startRow, endRow, preventViewNotify){
7874         if(this.locked) {
7875             return;
7876         }
7877         for(var i = startRow; i <= endRow; i++){
7878             this.deselectRow(i, preventViewNotify);
7879         }
7880     },
7881
7882     /**
7883      * Selects a row.
7884      * @param {Number} row The index of the row to select
7885      * @param {Boolean} keepExisting (optional) True to keep existing selections
7886      */
7887     selectRow : function(index, keepExisting, preventViewNotify){
7888         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7889             return;
7890         }
7891         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7892             if(!keepExisting || this.singleSelect){
7893                 this.clearSelections();
7894             }
7895             var r = this.grid.ds.getAt(index);
7896             this.selections.add(r);
7897             this.last = this.lastActive = index;
7898             if(!preventViewNotify){
7899                 var view = this.grid.view ? this.grid.view : this.grid;
7900                 view.onRowSelect(index);
7901             }
7902             this.fireEvent("rowselect", this, index, r);
7903             this.fireEvent("selectionchange", this);
7904         }
7905     },
7906
7907     /**
7908      * Deselects a row.
7909      * @param {Number} row The index of the row to deselect
7910      */
7911     deselectRow : function(index, preventViewNotify){
7912         if(this.locked) {
7913             return;
7914         }
7915         if(this.last == index){
7916             this.last = false;
7917         }
7918         if(this.lastActive == index){
7919             this.lastActive = false;
7920         }
7921         var r = this.grid.ds.getAt(index);
7922         this.selections.remove(r);
7923         if(!preventViewNotify){
7924             var view = this.grid.view ? this.grid.view : this.grid;
7925             view.onRowDeselect(index);
7926         }
7927         this.fireEvent("rowdeselect", this, index);
7928         this.fireEvent("selectionchange", this);
7929     },
7930
7931     // private
7932     restoreLast : function(){
7933         if(this._last){
7934             this.last = this._last;
7935         }
7936     },
7937
7938     // private
7939     acceptsNav : function(row, col, cm){
7940         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7941     },
7942
7943     // private
7944     onEditorKey : function(field, e){
7945         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7946         if(k == e.TAB){
7947             e.stopEvent();
7948             ed.completeEdit();
7949             if(e.shiftKey){
7950                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7951             }else{
7952                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7953             }
7954         }else if(k == e.ENTER && !e.ctrlKey){
7955             e.stopEvent();
7956             ed.completeEdit();
7957             if(e.shiftKey){
7958                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7959             }else{
7960                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7961             }
7962         }else if(k == e.ESC){
7963             ed.cancelEdit();
7964         }
7965         if(newCell){
7966             g.startEditing(newCell[0], newCell[1]);
7967         }
7968     }
7969 });/*
7970  * Based on:
7971  * Ext JS Library 1.1.1
7972  * Copyright(c) 2006-2007, Ext JS, LLC.
7973  *
7974  * Originally Released Under LGPL - original licence link has changed is not relivant.
7975  *
7976  * Fork - LGPL
7977  * <script type="text/javascript">
7978  */
7979  
7980
7981 /**
7982  * @class Roo.grid.ColumnModel
7983  * @extends Roo.util.Observable
7984  * This is the default implementation of a ColumnModel used by the Grid. It defines
7985  * the columns in the grid.
7986  * <br>Usage:<br>
7987  <pre><code>
7988  var colModel = new Roo.grid.ColumnModel([
7989         {header: "Ticker", width: 60, sortable: true, locked: true},
7990         {header: "Company Name", width: 150, sortable: true},
7991         {header: "Market Cap.", width: 100, sortable: true},
7992         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7993         {header: "Employees", width: 100, sortable: true, resizable: false}
7994  ]);
7995  </code></pre>
7996  * <p>
7997  
7998  * The config options listed for this class are options which may appear in each
7999  * individual column definition.
8000  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8001  * @constructor
8002  * @param {Object} config An Array of column config objects. See this class's
8003  * config objects for details.
8004 */
8005 Roo.grid.ColumnModel = function(config){
8006         /**
8007      * The config passed into the constructor
8008      */
8009     this.config = []; //config;
8010     this.lookup = {};
8011
8012     // if no id, create one
8013     // if the column does not have a dataIndex mapping,
8014     // map it to the order it is in the config
8015     for(var i = 0, len = config.length; i < len; i++){
8016         this.addColumn(config[i]);
8017         
8018     }
8019
8020     /**
8021      * The width of columns which have no width specified (defaults to 100)
8022      * @type Number
8023      */
8024     this.defaultWidth = 100;
8025
8026     /**
8027      * Default sortable of columns which have no sortable specified (defaults to false)
8028      * @type Boolean
8029      */
8030     this.defaultSortable = false;
8031
8032     this.addEvents({
8033         /**
8034              * @event widthchange
8035              * Fires when the width of a column changes.
8036              * @param {ColumnModel} this
8037              * @param {Number} columnIndex The column index
8038              * @param {Number} newWidth The new width
8039              */
8040             "widthchange": true,
8041         /**
8042              * @event headerchange
8043              * Fires when the text of a header changes.
8044              * @param {ColumnModel} this
8045              * @param {Number} columnIndex The column index
8046              * @param {Number} newText The new header text
8047              */
8048             "headerchange": true,
8049         /**
8050              * @event hiddenchange
8051              * Fires when a column is hidden or "unhidden".
8052              * @param {ColumnModel} this
8053              * @param {Number} columnIndex The column index
8054              * @param {Boolean} hidden true if hidden, false otherwise
8055              */
8056             "hiddenchange": true,
8057             /**
8058          * @event columnmoved
8059          * Fires when a column is moved.
8060          * @param {ColumnModel} this
8061          * @param {Number} oldIndex
8062          * @param {Number} newIndex
8063          */
8064         "columnmoved" : true,
8065         /**
8066          * @event columlockchange
8067          * Fires when a column's locked state is changed
8068          * @param {ColumnModel} this
8069          * @param {Number} colIndex
8070          * @param {Boolean} locked true if locked
8071          */
8072         "columnlockchange" : true
8073     });
8074     Roo.grid.ColumnModel.superclass.constructor.call(this);
8075 };
8076 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8077     /**
8078      * @cfg {String} header The header text to display in the Grid view.
8079      */
8080         /**
8081      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8082      */
8083         /**
8084      * @cfg {String} smHeader Header at Bootsrap Small width
8085      */
8086         /**
8087      * @cfg {String} mdHeader Header at Bootsrap Medium width
8088      */
8089         /**
8090      * @cfg {String} lgHeader Header at Bootsrap Large width
8091      */
8092         /**
8093      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8094      */
8095     /**
8096      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8097      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8098      * specified, the column's index is used as an index into the Record's data Array.
8099      */
8100     /**
8101      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8102      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8103      */
8104     /**
8105      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8106      * Defaults to the value of the {@link #defaultSortable} property.
8107      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8108      */
8109     /**
8110      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8111      */
8112     /**
8113      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8114      */
8115     /**
8116      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8117      */
8118     /**
8119      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8120      */
8121     /**
8122      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8123      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8124      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8125      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8126      */
8127        /**
8128      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8129      */
8130     /**
8131      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8132      */
8133     /**
8134      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8135      */
8136     /**
8137      * @cfg {String} cursor (Optional)
8138      */
8139     /**
8140      * @cfg {String} tooltip (Optional)
8141      */
8142     /**
8143      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8144      */
8145     /**
8146      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8147      */
8148     /**
8149      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8150      */
8151     /**
8152      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8153      */
8154         /**
8155      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8156      */
8157     /**
8158      * Returns the id of the column at the specified index.
8159      * @param {Number} index The column index
8160      * @return {String} the id
8161      */
8162     getColumnId : function(index){
8163         return this.config[index].id;
8164     },
8165
8166     /**
8167      * Returns the column for a specified id.
8168      * @param {String} id The column id
8169      * @return {Object} the column
8170      */
8171     getColumnById : function(id){
8172         return this.lookup[id];
8173     },
8174
8175     
8176     /**
8177      * Returns the column Object for a specified dataIndex.
8178      * @param {String} dataIndex The column dataIndex
8179      * @return {Object|Boolean} the column or false if not found
8180      */
8181     getColumnByDataIndex: function(dataIndex){
8182         var index = this.findColumnIndex(dataIndex);
8183         return index > -1 ? this.config[index] : false;
8184     },
8185     
8186     /**
8187      * Returns the index for a specified column id.
8188      * @param {String} id The column id
8189      * @return {Number} the index, or -1 if not found
8190      */
8191     getIndexById : function(id){
8192         for(var i = 0, len = this.config.length; i < len; i++){
8193             if(this.config[i].id == id){
8194                 return i;
8195             }
8196         }
8197         return -1;
8198     },
8199     
8200     /**
8201      * Returns the index for a specified column dataIndex.
8202      * @param {String} dataIndex The column dataIndex
8203      * @return {Number} the index, or -1 if not found
8204      */
8205     
8206     findColumnIndex : function(dataIndex){
8207         for(var i = 0, len = this.config.length; i < len; i++){
8208             if(this.config[i].dataIndex == dataIndex){
8209                 return i;
8210             }
8211         }
8212         return -1;
8213     },
8214     
8215     
8216     moveColumn : function(oldIndex, newIndex){
8217         var c = this.config[oldIndex];
8218         this.config.splice(oldIndex, 1);
8219         this.config.splice(newIndex, 0, c);
8220         this.dataMap = null;
8221         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8222     },
8223
8224     isLocked : function(colIndex){
8225         return this.config[colIndex].locked === true;
8226     },
8227
8228     setLocked : function(colIndex, value, suppressEvent){
8229         if(this.isLocked(colIndex) == value){
8230             return;
8231         }
8232         this.config[colIndex].locked = value;
8233         if(!suppressEvent){
8234             this.fireEvent("columnlockchange", this, colIndex, value);
8235         }
8236     },
8237
8238     getTotalLockedWidth : function(){
8239         var totalWidth = 0;
8240         for(var i = 0; i < this.config.length; i++){
8241             if(this.isLocked(i) && !this.isHidden(i)){
8242                 this.totalWidth += this.getColumnWidth(i);
8243             }
8244         }
8245         return totalWidth;
8246     },
8247
8248     getLockedCount : function(){
8249         for(var i = 0, len = this.config.length; i < len; i++){
8250             if(!this.isLocked(i)){
8251                 return i;
8252             }
8253         }
8254         
8255         return this.config.length;
8256     },
8257
8258     /**
8259      * Returns the number of columns.
8260      * @return {Number}
8261      */
8262     getColumnCount : function(visibleOnly){
8263         if(visibleOnly === true){
8264             var c = 0;
8265             for(var i = 0, len = this.config.length; i < len; i++){
8266                 if(!this.isHidden(i)){
8267                     c++;
8268                 }
8269             }
8270             return c;
8271         }
8272         return this.config.length;
8273     },
8274
8275     /**
8276      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8277      * @param {Function} fn
8278      * @param {Object} scope (optional)
8279      * @return {Array} result
8280      */
8281     getColumnsBy : function(fn, scope){
8282         var r = [];
8283         for(var i = 0, len = this.config.length; i < len; i++){
8284             var c = this.config[i];
8285             if(fn.call(scope||this, c, i) === true){
8286                 r[r.length] = c;
8287             }
8288         }
8289         return r;
8290     },
8291
8292     /**
8293      * Returns true if the specified column is sortable.
8294      * @param {Number} col The column index
8295      * @return {Boolean}
8296      */
8297     isSortable : function(col){
8298         if(typeof this.config[col].sortable == "undefined"){
8299             return this.defaultSortable;
8300         }
8301         return this.config[col].sortable;
8302     },
8303
8304     /**
8305      * Returns the rendering (formatting) function defined for the column.
8306      * @param {Number} col The column index.
8307      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8308      */
8309     getRenderer : function(col){
8310         if(!this.config[col].renderer){
8311             return Roo.grid.ColumnModel.defaultRenderer;
8312         }
8313         return this.config[col].renderer;
8314     },
8315
8316     /**
8317      * Sets the rendering (formatting) function for a column.
8318      * @param {Number} col The column index
8319      * @param {Function} fn The function to use to process the cell's raw data
8320      * to return HTML markup for the grid view. The render function is called with
8321      * the following parameters:<ul>
8322      * <li>Data value.</li>
8323      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8324      * <li>css A CSS style string to apply to the table cell.</li>
8325      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8326      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8327      * <li>Row index</li>
8328      * <li>Column index</li>
8329      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8330      */
8331     setRenderer : function(col, fn){
8332         this.config[col].renderer = fn;
8333     },
8334
8335     /**
8336      * Returns the width for the specified column.
8337      * @param {Number} col The column index
8338      * @param (optional) {String} gridSize bootstrap width size.
8339      * @return {Number}
8340      */
8341     getColumnWidth : function(col, gridSize)
8342         {
8343                 var cfg = this.config[col];
8344                 
8345                 if (typeof(gridSize) == 'undefined') {
8346                         return cfg.width * 1 || this.defaultWidth;
8347                 }
8348                 if (gridSize === false) { // if we set it..
8349                         return cfg.width || false;
8350                 }
8351                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8352                 
8353                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8354                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8355                                 continue;
8356                         }
8357                         return cfg[ sizes[i] ];
8358                 }
8359                 return 1;
8360                 
8361     },
8362
8363     /**
8364      * Sets the width for a column.
8365      * @param {Number} col The column index
8366      * @param {Number} width The new width
8367      */
8368     setColumnWidth : function(col, width, suppressEvent){
8369         this.config[col].width = width;
8370         this.totalWidth = null;
8371         if(!suppressEvent){
8372              this.fireEvent("widthchange", this, col, width);
8373         }
8374     },
8375
8376     /**
8377      * Returns the total width of all columns.
8378      * @param {Boolean} includeHidden True to include hidden column widths
8379      * @return {Number}
8380      */
8381     getTotalWidth : function(includeHidden){
8382         if(!this.totalWidth){
8383             this.totalWidth = 0;
8384             for(var i = 0, len = this.config.length; i < len; i++){
8385                 if(includeHidden || !this.isHidden(i)){
8386                     this.totalWidth += this.getColumnWidth(i);
8387                 }
8388             }
8389         }
8390         return this.totalWidth;
8391     },
8392
8393     /**
8394      * Returns the header for the specified column.
8395      * @param {Number} col The column index
8396      * @return {String}
8397      */
8398     getColumnHeader : function(col){
8399         return this.config[col].header;
8400     },
8401
8402     /**
8403      * Sets the header for a column.
8404      * @param {Number} col The column index
8405      * @param {String} header The new header
8406      */
8407     setColumnHeader : function(col, header){
8408         this.config[col].header = header;
8409         this.fireEvent("headerchange", this, col, header);
8410     },
8411
8412     /**
8413      * Returns the tooltip for the specified column.
8414      * @param {Number} col The column index
8415      * @return {String}
8416      */
8417     getColumnTooltip : function(col){
8418             return this.config[col].tooltip;
8419     },
8420     /**
8421      * Sets the tooltip for a column.
8422      * @param {Number} col The column index
8423      * @param {String} tooltip The new tooltip
8424      */
8425     setColumnTooltip : function(col, tooltip){
8426             this.config[col].tooltip = tooltip;
8427     },
8428
8429     /**
8430      * Returns the dataIndex for the specified column.
8431      * @param {Number} col The column index
8432      * @return {Number}
8433      */
8434     getDataIndex : function(col){
8435         return this.config[col].dataIndex;
8436     },
8437
8438     /**
8439      * Sets the dataIndex for a column.
8440      * @param {Number} col The column index
8441      * @param {Number} dataIndex The new dataIndex
8442      */
8443     setDataIndex : function(col, dataIndex){
8444         this.config[col].dataIndex = dataIndex;
8445     },
8446
8447     
8448     
8449     /**
8450      * Returns true if the cell is editable.
8451      * @param {Number} colIndex The column index
8452      * @param {Number} rowIndex The row index - this is nto actually used..?
8453      * @return {Boolean}
8454      */
8455     isCellEditable : function(colIndex, rowIndex){
8456         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8457     },
8458
8459     /**
8460      * Returns the editor defined for the cell/column.
8461      * return false or null to disable editing.
8462      * @param {Number} colIndex The column index
8463      * @param {Number} rowIndex The row index
8464      * @return {Object}
8465      */
8466     getCellEditor : function(colIndex, rowIndex){
8467         return this.config[colIndex].editor;
8468     },
8469
8470     /**
8471      * Sets if a column is editable.
8472      * @param {Number} col The column index
8473      * @param {Boolean} editable True if the column is editable
8474      */
8475     setEditable : function(col, editable){
8476         this.config[col].editable = editable;
8477     },
8478
8479
8480     /**
8481      * Returns true if the column is hidden.
8482      * @param {Number} colIndex The column index
8483      * @return {Boolean}
8484      */
8485     isHidden : function(colIndex){
8486         return this.config[colIndex].hidden;
8487     },
8488
8489
8490     /**
8491      * Returns true if the column width cannot be changed
8492      */
8493     isFixed : function(colIndex){
8494         return this.config[colIndex].fixed;
8495     },
8496
8497     /**
8498      * Returns true if the column can be resized
8499      * @return {Boolean}
8500      */
8501     isResizable : function(colIndex){
8502         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8503     },
8504     /**
8505      * Sets if a column is hidden.
8506      * @param {Number} colIndex The column index
8507      * @param {Boolean} hidden True if the column is hidden
8508      */
8509     setHidden : function(colIndex, hidden){
8510         this.config[colIndex].hidden = hidden;
8511         this.totalWidth = null;
8512         this.fireEvent("hiddenchange", this, colIndex, hidden);
8513     },
8514
8515     /**
8516      * Sets the editor for a column.
8517      * @param {Number} col The column index
8518      * @param {Object} editor The editor object
8519      */
8520     setEditor : function(col, editor){
8521         this.config[col].editor = editor;
8522     },
8523     /**
8524      * Add a column (experimental...) - defaults to adding to the end..
8525      * @param {Object} config 
8526     */
8527     addColumn : function(c)
8528     {
8529     
8530         var i = this.config.length;
8531         this.config[i] = c;
8532         
8533         if(typeof c.dataIndex == "undefined"){
8534             c.dataIndex = i;
8535         }
8536         if(typeof c.renderer == "string"){
8537             c.renderer = Roo.util.Format[c.renderer];
8538         }
8539         if(typeof c.id == "undefined"){
8540             c.id = Roo.id();
8541         }
8542         if(c.editor && c.editor.xtype){
8543             c.editor  = Roo.factory(c.editor, Roo.grid);
8544         }
8545         if(c.editor && c.editor.isFormField){
8546             c.editor = new Roo.grid.GridEditor(c.editor);
8547         }
8548         this.lookup[c.id] = c;
8549     }
8550     
8551 });
8552
8553 Roo.grid.ColumnModel.defaultRenderer = function(value)
8554 {
8555     if(typeof value == "object") {
8556         return value;
8557     }
8558         if(typeof value == "string" && value.length < 1){
8559             return "&#160;";
8560         }
8561     
8562         return String.format("{0}", value);
8563 };
8564
8565 // Alias for backwards compatibility
8566 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8567 /*
8568  * Based on:
8569  * Ext JS Library 1.1.1
8570  * Copyright(c) 2006-2007, Ext JS, LLC.
8571  *
8572  * Originally Released Under LGPL - original licence link has changed is not relivant.
8573  *
8574  * Fork - LGPL
8575  * <script type="text/javascript">
8576  */
8577  
8578 /**
8579  * @class Roo.LoadMask
8580  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8581  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8582  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8583  * element's UpdateManager load indicator and will be destroyed after the initial load.
8584  * @constructor
8585  * Create a new LoadMask
8586  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8587  * @param {Object} config The config object
8588  */
8589 Roo.LoadMask = function(el, config){
8590     this.el = Roo.get(el);
8591     Roo.apply(this, config);
8592     if(this.store){
8593         this.store.on('beforeload', this.onBeforeLoad, this);
8594         this.store.on('load', this.onLoad, this);
8595         this.store.on('loadexception', this.onLoadException, this);
8596         this.removeMask = false;
8597     }else{
8598         var um = this.el.getUpdateManager();
8599         um.showLoadIndicator = false; // disable the default indicator
8600         um.on('beforeupdate', this.onBeforeLoad, this);
8601         um.on('update', this.onLoad, this);
8602         um.on('failure', this.onLoad, this);
8603         this.removeMask = true;
8604     }
8605 };
8606
8607 Roo.LoadMask.prototype = {
8608     /**
8609      * @cfg {Boolean} removeMask
8610      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8611      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8612      */
8613     removeMask : false,
8614     /**
8615      * @cfg {String} msg
8616      * The text to display in a centered loading message box (defaults to 'Loading...')
8617      */
8618     msg : 'Loading...',
8619     /**
8620      * @cfg {String} msgCls
8621      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8622      */
8623     msgCls : 'x-mask-loading',
8624
8625     /**
8626      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8627      * @type Boolean
8628      */
8629     disabled: false,
8630
8631     /**
8632      * Disables the mask to prevent it from being displayed
8633      */
8634     disable : function(){
8635        this.disabled = true;
8636     },
8637
8638     /**
8639      * Enables the mask so that it can be displayed
8640      */
8641     enable : function(){
8642         this.disabled = false;
8643     },
8644     
8645     onLoadException : function()
8646     {
8647         Roo.log(arguments);
8648         
8649         if (typeof(arguments[3]) != 'undefined') {
8650             Roo.MessageBox.alert("Error loading",arguments[3]);
8651         } 
8652         /*
8653         try {
8654             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8655                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8656             }   
8657         } catch(e) {
8658             
8659         }
8660         */
8661     
8662         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8663     },
8664     // private
8665     onLoad : function()
8666     {
8667         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8668     },
8669
8670     // private
8671     onBeforeLoad : function(){
8672         if(!this.disabled){
8673             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8674         }
8675     },
8676
8677     // private
8678     destroy : function(){
8679         if(this.store){
8680             this.store.un('beforeload', this.onBeforeLoad, this);
8681             this.store.un('load', this.onLoad, this);
8682             this.store.un('loadexception', this.onLoadException, this);
8683         }else{
8684             var um = this.el.getUpdateManager();
8685             um.un('beforeupdate', this.onBeforeLoad, this);
8686             um.un('update', this.onLoad, this);
8687             um.un('failure', this.onLoad, this);
8688         }
8689     }
8690 };/**
8691  * @class Roo.bootstrap.Table
8692  * @licence LGBL
8693  * @extends Roo.bootstrap.Component
8694  * @children Roo.bootstrap.TableBody
8695  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8696  * Similar to Roo.grid.Grid
8697  * <pre><code>
8698  var table = Roo.factory({
8699     xtype : 'Table',
8700     xns : Roo.bootstrap,
8701     autoSizeColumns: true,
8702     
8703     
8704     store : {
8705         xtype : 'Store',
8706         xns : Roo.data,
8707         remoteSort : true,
8708         sortInfo : { direction : 'ASC', field: 'name' },
8709         proxy : {
8710            xtype : 'HttpProxy',
8711            xns : Roo.data,
8712            method : 'GET',
8713            url : 'https://example.com/some.data.url.json'
8714         },
8715         reader : {
8716            xtype : 'JsonReader',
8717            xns : Roo.data,
8718            fields : [ 'id', 'name', whatever' ],
8719            id : 'id',
8720            root : 'data'
8721         }
8722     },
8723     cm : [
8724         {
8725             xtype : 'ColumnModel',
8726             xns : Roo.grid,
8727             align : 'center',
8728             cursor : 'pointer',
8729             dataIndex : 'is_in_group',
8730             header : "Name",
8731             sortable : true,
8732             renderer : function(v, x , r) {  
8733             
8734                 return String.format("{0}", v)
8735             }
8736             width : 3
8737         } // more columns..
8738     ],
8739     selModel : {
8740         xtype : 'RowSelectionModel',
8741         xns : Roo.bootstrap.Table
8742         // you can add listeners to catch selection change here....
8743     }
8744      
8745
8746  });
8747  // set any options
8748  grid.render(Roo.get("some-div"));
8749 </code></pre>
8750
8751 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8752
8753
8754
8755  *
8756  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8757  * @cfg {Roo.data.Store} store The data store to use
8758  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
8759  * 
8760  * @cfg {String} cls table class
8761  *
8762  * 
8763  * @cfg {boolean} striped Should the rows be alternative striped
8764  * @cfg {boolean} bordered Add borders to the table
8765  * @cfg {boolean} hover Add hover highlighting
8766  * @cfg {boolean} condensed Format condensed
8767  * @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,
8768  *                also adds table-responsive (see bootstrap docs for details)
8769  * @cfg {Boolean} loadMask (true|false) default false
8770  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8771  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8772  * @cfg {Boolean} rowSelection (true|false) default false
8773  * @cfg {Boolean} cellSelection (true|false) default false
8774  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8775  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8776  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8777  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8778  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8779  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8780  * 
8781  * @constructor
8782  * Create a new Table
8783  * @param {Object} config The config object
8784  */
8785
8786 Roo.bootstrap.Table = function(config)
8787 {
8788     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8789      
8790     // BC...
8791     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8792     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8793     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8794     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8795     
8796     this.view = this; // compat with grid.
8797     
8798     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8799     if (this.sm) {
8800         this.sm.grid = this;
8801         this.selModel = Roo.factory(this.sm, Roo.grid);
8802         this.sm = this.selModel;
8803         this.sm.xmodule = this.xmodule || false;
8804     }
8805     
8806     if (this.cm && typeof(this.cm.config) == 'undefined') {
8807         this.colModel = new Roo.grid.ColumnModel(this.cm);
8808         this.cm = this.colModel;
8809         this.cm.xmodule = this.xmodule || false;
8810     }
8811     if (this.store) {
8812         this.store= Roo.factory(this.store, Roo.data);
8813         this.ds = this.store;
8814         this.ds.xmodule = this.xmodule || false;
8815          
8816     }
8817     if (this.footer && this.store) {
8818         this.footer.dataSource = this.ds;
8819         this.footer = Roo.factory(this.footer);
8820     }
8821     
8822     /** @private */
8823     this.addEvents({
8824         /**
8825          * @event cellclick
8826          * Fires when a cell is clicked
8827          * @param {Roo.bootstrap.Table} this
8828          * @param {Roo.Element} el
8829          * @param {Number} rowIndex
8830          * @param {Number} columnIndex
8831          * @param {Roo.EventObject} e
8832          */
8833         "cellclick" : true,
8834         /**
8835          * @event celldblclick
8836          * Fires when a cell is double clicked
8837          * @param {Roo.bootstrap.Table} this
8838          * @param {Roo.Element} el
8839          * @param {Number} rowIndex
8840          * @param {Number} columnIndex
8841          * @param {Roo.EventObject} e
8842          */
8843         "celldblclick" : true,
8844         /**
8845          * @event rowclick
8846          * Fires when a row is clicked
8847          * @param {Roo.bootstrap.Table} this
8848          * @param {Roo.Element} el
8849          * @param {Number} rowIndex
8850          * @param {Roo.EventObject} e
8851          */
8852         "rowclick" : true,
8853         /**
8854          * @event rowdblclick
8855          * Fires when a row is double clicked
8856          * @param {Roo.bootstrap.Table} this
8857          * @param {Roo.Element} el
8858          * @param {Number} rowIndex
8859          * @param {Roo.EventObject} e
8860          */
8861         "rowdblclick" : true,
8862         /**
8863          * @event mouseover
8864          * Fires when a mouseover occur
8865          * @param {Roo.bootstrap.Table} this
8866          * @param {Roo.Element} el
8867          * @param {Number} rowIndex
8868          * @param {Number} columnIndex
8869          * @param {Roo.EventObject} e
8870          */
8871         "mouseover" : true,
8872         /**
8873          * @event mouseout
8874          * Fires when a mouseout occur
8875          * @param {Roo.bootstrap.Table} this
8876          * @param {Roo.Element} el
8877          * @param {Number} rowIndex
8878          * @param {Number} columnIndex
8879          * @param {Roo.EventObject} e
8880          */
8881         "mouseout" : true,
8882         /**
8883          * @event rowclass
8884          * Fires when a row is rendered, so you can change add a style to it.
8885          * @param {Roo.bootstrap.Table} this
8886          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8887          */
8888         'rowclass' : true,
8889           /**
8890          * @event rowsrendered
8891          * Fires when all the  rows have been rendered
8892          * @param {Roo.bootstrap.Table} this
8893          */
8894         'rowsrendered' : true,
8895         /**
8896          * @event contextmenu
8897          * The raw contextmenu event for the entire grid.
8898          * @param {Roo.EventObject} e
8899          */
8900         "contextmenu" : true,
8901         /**
8902          * @event rowcontextmenu
8903          * Fires when a row is right clicked
8904          * @param {Roo.bootstrap.Table} this
8905          * @param {Number} rowIndex
8906          * @param {Roo.EventObject} e
8907          */
8908         "rowcontextmenu" : true,
8909         /**
8910          * @event cellcontextmenu
8911          * Fires when a cell is right clicked
8912          * @param {Roo.bootstrap.Table} this
8913          * @param {Number} rowIndex
8914          * @param {Number} cellIndex
8915          * @param {Roo.EventObject} e
8916          */
8917          "cellcontextmenu" : true,
8918          /**
8919          * @event headercontextmenu
8920          * Fires when a header is right clicked
8921          * @param {Roo.bootstrap.Table} this
8922          * @param {Number} columnIndex
8923          * @param {Roo.EventObject} e
8924          */
8925         "headercontextmenu" : true,
8926         /**
8927          * @event mousedown
8928          * The raw mousedown event for the entire grid.
8929          * @param {Roo.EventObject} e
8930          */
8931         "mousedown" : true
8932         
8933     });
8934 };
8935
8936 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8937     
8938     cls: false,
8939     
8940     striped : false,
8941     scrollBody : false,
8942     bordered: false,
8943     hover:  false,
8944     condensed : false,
8945     responsive : false,
8946     sm : false,
8947     cm : false,
8948     store : false,
8949     loadMask : false,
8950     footerShow : true,
8951     headerShow : true,
8952     enableColumnResize: true,
8953   
8954     rowSelection : false,
8955     cellSelection : false,
8956     layout : false,
8957
8958     minColumnWidth : 50,
8959     
8960     // Roo.Element - the tbody
8961     bodyEl: false,  // <tbody> Roo.Element - thead element    
8962     headEl: false,  // <thead> Roo.Element - thead element
8963     resizeProxy : false, // proxy element for dragging?
8964
8965
8966     
8967     container: false, // used by gridpanel...
8968     
8969     lazyLoad : false,
8970     
8971     CSS : Roo.util.CSS,
8972     
8973     auto_hide_footer : false,
8974     
8975     view: false, // actually points to this..
8976     
8977     getAutoCreate : function()
8978     {
8979         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8980         
8981         cfg = {
8982             tag: 'table',
8983             cls : 'table', 
8984             cn : []
8985         };
8986         // this get's auto added by panel.Grid
8987         if (this.scrollBody) {
8988             cfg.cls += ' table-body-fixed';
8989         }    
8990         if (this.striped) {
8991             cfg.cls += ' table-striped';
8992         }
8993         
8994         if (this.hover) {
8995             cfg.cls += ' table-hover';
8996         }
8997         if (this.bordered) {
8998             cfg.cls += ' table-bordered';
8999         }
9000         if (this.condensed) {
9001             cfg.cls += ' table-condensed';
9002         }
9003         
9004         if (this.responsive) {
9005             cfg.cls += ' table-responsive';
9006         }
9007         
9008         if (this.cls) {
9009             cfg.cls+=  ' ' +this.cls;
9010         }
9011         
9012         
9013         
9014         if (this.layout) {
9015             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9016         }
9017         
9018         if(this.store || this.cm){
9019             if(this.headerShow){
9020                 cfg.cn.push(this.renderHeader());
9021             }
9022             
9023             cfg.cn.push(this.renderBody());
9024             
9025             if(this.footerShow){
9026                 cfg.cn.push(this.renderFooter());
9027             }
9028             // where does this come from?
9029             //cfg.cls+=  ' TableGrid';
9030         }
9031         
9032         return { cn : [ cfg ] };
9033     },
9034     
9035     initEvents : function()
9036     {   
9037         if(!this.store || !this.cm){
9038             return;
9039         }
9040         if (this.selModel) {
9041             this.selModel.initEvents();
9042         }
9043         
9044         
9045         //Roo.log('initEvents with ds!!!!');
9046         
9047         this.bodyEl = this.el.select('tbody', true).first();
9048         this.headEl = this.el.select('thead', true).first();
9049         this.mainFoot = this.el.select('tfoot', true).first();
9050         
9051         
9052         
9053         
9054         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9055             e.on('click', this.sort, this);
9056         }, this);
9057         
9058         
9059         // why is this done????? = it breaks dialogs??
9060         //this.parent().el.setStyle('position', 'relative');
9061         
9062         
9063         if (this.footer) {
9064             this.footer.parentId = this.id;
9065             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9066             
9067             if(this.lazyLoad){
9068                 this.el.select('tfoot tr td').first().addClass('hide');
9069             }
9070         } 
9071         
9072         if(this.loadMask) {
9073             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9074         }
9075         
9076         this.store.on('load', this.onLoad, this);
9077         this.store.on('beforeload', this.onBeforeLoad, this);
9078         this.store.on('update', this.onUpdate, this);
9079         this.store.on('add', this.onAdd, this);
9080         this.store.on("clear", this.clear, this);
9081         
9082         this.el.on("contextmenu", this.onContextMenu, this);
9083         
9084         
9085         this.cm.on("headerchange", this.onHeaderChange, this);
9086         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9087
9088  //?? does bodyEl get replaced on render?
9089         this.bodyEl.on("click", this.onClick, this);
9090         this.bodyEl.on("dblclick", this.onDblClick, this);        
9091         this.bodyEl.on('scroll', this.onBodyScroll, this);
9092
9093         // guessing mainbody will work - this relays usually caught by selmodel at present.
9094         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9095   
9096   
9097         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9098         
9099   
9100         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9101             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9102         }
9103         
9104         this.initCSS();
9105     },
9106     // Compatibility with grid - we implement all the view features at present.
9107     getView : function()
9108     {
9109         return this;
9110     },
9111     
9112     initCSS : function()
9113     {
9114         
9115         
9116         var cm = this.cm, styles = [];
9117         this.CSS.removeStyleSheet(this.id + '-cssrules');
9118         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9119         // we can honour xs/sm/md/xl  as widths...
9120         // we first have to decide what widht we are currently at...
9121         var sz = Roo.getGridSize();
9122         
9123         var total = 0;
9124         var last = -1;
9125         var cols = []; // visable cols.
9126         var total_abs = 0;
9127         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9128             var w = cm.getColumnWidth(i, false);
9129             if(cm.isHidden(i)){
9130                 cols.push( { rel : false, abs : 0 });
9131                 continue;
9132             }
9133             if (w !== false) {
9134                 cols.push( { rel : false, abs : w });
9135                 total_abs += w;
9136                 last = i; // not really..
9137                 continue;
9138             }
9139             var w = cm.getColumnWidth(i, sz);
9140             if (w > 0) {
9141                 last = i
9142             }
9143             total += w;
9144             cols.push( { rel : w, abs : false });
9145         }
9146         
9147         var avail = this.bodyEl.dom.clientWidth - total_abs;
9148         
9149         var unitWidth = Math.floor(avail / total);
9150         var rem = avail - (unitWidth * total);
9151         
9152         var hidden, width, pos = 0 , splithide , left;
9153         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9154             
9155             hidden = 'display:none;';
9156             left = '';
9157             width  = 'width:0px;';
9158             splithide = '';
9159             if(!cm.isHidden(i)){
9160                 hidden = '';
9161                 
9162                 
9163                 // we can honour xs/sm/md/xl ?
9164                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9165                 if (w===0) {
9166                     hidden = 'display:none;';
9167                 }
9168                 // width should return a small number...
9169                 if (i == last) {
9170                     w+=rem; // add the remaining with..
9171                 }
9172                 pos += w;
9173                 left = "left:" + (pos -4) + "px;";
9174                 width = "width:" + w+ "px;";
9175                 
9176             }
9177             if (this.responsive) {
9178                 width = '';
9179                 left = '';
9180                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9181                 splithide = 'display: none;';
9182             }
9183             
9184             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9185             if (this.headEl) {
9186                 if (i == last) {
9187                     splithide = 'display:none;';
9188                 }
9189                 
9190                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9191                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9192                 );
9193             }
9194             
9195         }
9196         //Roo.log(styles.join(''));
9197         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9198         
9199     },
9200     
9201     
9202     
9203     onContextMenu : function(e, t)
9204     {
9205         this.processEvent("contextmenu", e);
9206     },
9207     
9208     processEvent : function(name, e)
9209     {
9210         if (name != 'touchstart' ) {
9211             this.fireEvent(name, e);    
9212         }
9213         
9214         var t = e.getTarget();
9215         
9216         var cell = Roo.get(t);
9217         
9218         if(!cell){
9219             return;
9220         }
9221         
9222         if(cell.findParent('tfoot', false, true)){
9223             return;
9224         }
9225         
9226         if(cell.findParent('thead', false, true)){
9227             
9228             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9229                 cell = Roo.get(t).findParent('th', false, true);
9230                 if (!cell) {
9231                     Roo.log("failed to find th in thead?");
9232                     Roo.log(e.getTarget());
9233                     return;
9234                 }
9235             }
9236             
9237             var cellIndex = cell.dom.cellIndex;
9238             
9239             var ename = name == 'touchstart' ? 'click' : name;
9240             this.fireEvent("header" + ename, this, cellIndex, e);
9241             
9242             return;
9243         }
9244         
9245         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9246             cell = Roo.get(t).findParent('td', false, true);
9247             if (!cell) {
9248                 Roo.log("failed to find th in tbody?");
9249                 Roo.log(e.getTarget());
9250                 return;
9251             }
9252         }
9253         
9254         var row = cell.findParent('tr', false, true);
9255         var cellIndex = cell.dom.cellIndex;
9256         var rowIndex = row.dom.rowIndex - 1;
9257         
9258         if(row !== false){
9259             
9260             this.fireEvent("row" + name, this, rowIndex, e);
9261             
9262             if(cell !== false){
9263             
9264                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9265             }
9266         }
9267         
9268     },
9269     
9270     onMouseover : function(e, el)
9271     {
9272         var cell = Roo.get(el);
9273         
9274         if(!cell){
9275             return;
9276         }
9277         
9278         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9279             cell = cell.findParent('td', false, true);
9280         }
9281         
9282         var row = cell.findParent('tr', false, true);
9283         var cellIndex = cell.dom.cellIndex;
9284         var rowIndex = row.dom.rowIndex - 1; // start from 0
9285         
9286         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9287         
9288     },
9289     
9290     onMouseout : function(e, el)
9291     {
9292         var cell = Roo.get(el);
9293         
9294         if(!cell){
9295             return;
9296         }
9297         
9298         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9299             cell = cell.findParent('td', false, true);
9300         }
9301         
9302         var row = cell.findParent('tr', false, true);
9303         var cellIndex = cell.dom.cellIndex;
9304         var rowIndex = row.dom.rowIndex - 1; // start from 0
9305         
9306         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9307         
9308     },
9309     
9310     onClick : function(e, el)
9311     {
9312         var cell = Roo.get(el);
9313         
9314         if(!cell || (!this.cellSelection && !this.rowSelection)){
9315             return;
9316         }
9317         
9318         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9319             cell = cell.findParent('td', false, true);
9320         }
9321         
9322         if(!cell || typeof(cell) == 'undefined'){
9323             return;
9324         }
9325         
9326         var row = cell.findParent('tr', false, true);
9327         
9328         if(!row || typeof(row) == 'undefined'){
9329             return;
9330         }
9331         
9332         var cellIndex = cell.dom.cellIndex;
9333         var rowIndex = this.getRowIndex(row);
9334         
9335         // why??? - should these not be based on SelectionModel?
9336         //if(this.cellSelection){
9337             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9338         //}
9339         
9340         //if(this.rowSelection){
9341             this.fireEvent('rowclick', this, row, rowIndex, e);
9342         //}
9343          
9344     },
9345         
9346     onDblClick : function(e,el)
9347     {
9348         var cell = Roo.get(el);
9349         
9350         if(!cell || (!this.cellSelection && !this.rowSelection)){
9351             return;
9352         }
9353         
9354         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9355             cell = cell.findParent('td', false, true);
9356         }
9357         
9358         if(!cell || typeof(cell) == 'undefined'){
9359             return;
9360         }
9361         
9362         var row = cell.findParent('tr', false, true);
9363         
9364         if(!row || typeof(row) == 'undefined'){
9365             return;
9366         }
9367         
9368         var cellIndex = cell.dom.cellIndex;
9369         var rowIndex = this.getRowIndex(row);
9370         
9371         if(this.cellSelection){
9372             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9373         }
9374         
9375         if(this.rowSelection){
9376             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9377         }
9378     },
9379     findRowIndex : function(el)
9380     {
9381         var cell = Roo.get(el);
9382         if(!cell) {
9383             return false;
9384         }
9385         var row = cell.findParent('tr', false, true);
9386         
9387         if(!row || typeof(row) == 'undefined'){
9388             return false;
9389         }
9390         return this.getRowIndex(row);
9391     },
9392     sort : function(e,el)
9393     {
9394         var col = Roo.get(el);
9395         
9396         if(!col.hasClass('sortable')){
9397             return;
9398         }
9399         
9400         var sort = col.attr('sort');
9401         var dir = 'ASC';
9402         
9403         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9404             dir = 'DESC';
9405         }
9406         
9407         this.store.sortInfo = {field : sort, direction : dir};
9408         
9409         if (this.footer) {
9410             Roo.log("calling footer first");
9411             this.footer.onClick('first');
9412         } else {
9413         
9414             this.store.load({ params : { start : 0 } });
9415         }
9416     },
9417     
9418     renderHeader : function()
9419     {
9420         var header = {
9421             tag: 'thead',
9422             cn : []
9423         };
9424         
9425         var cm = this.cm;
9426         this.totalWidth = 0;
9427         
9428         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9429             
9430             var config = cm.config[i];
9431             
9432             var c = {
9433                 tag: 'th',
9434                 cls : 'x-hcol-' + i,
9435                 style : '',
9436                 
9437                 html: cm.getColumnHeader(i)
9438             };
9439             
9440             var tooltip = cm.getColumnTooltip(i);
9441             if (tooltip) {
9442                 c.tooltip = tooltip;
9443             }
9444             
9445             
9446             var hh = '';
9447             
9448             if(typeof(config.sortable) != 'undefined' && config.sortable){
9449                 c.cls += ' sortable';
9450                 c.html = '<i class="fa"></i>' + c.html;
9451             }
9452             
9453             // could use BS4 hidden-..-down 
9454             
9455             if(typeof(config.lgHeader) != 'undefined'){
9456                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9457             }
9458             
9459             if(typeof(config.mdHeader) != 'undefined'){
9460                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9461             }
9462             
9463             if(typeof(config.smHeader) != 'undefined'){
9464                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9465             }
9466             
9467             if(typeof(config.xsHeader) != 'undefined'){
9468                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9469             }
9470             
9471             if(hh.length){
9472                 c.html = hh;
9473             }
9474             
9475             if(typeof(config.tooltip) != 'undefined'){
9476                 c.tooltip = config.tooltip;
9477             }
9478             
9479             if(typeof(config.colspan) != 'undefined'){
9480                 c.colspan = config.colspan;
9481             }
9482             
9483             // hidden is handled by CSS now
9484             
9485             if(typeof(config.dataIndex) != 'undefined'){
9486                 c.sort = config.dataIndex;
9487             }
9488             
9489            
9490             
9491             if(typeof(config.align) != 'undefined' && config.align.length){
9492                 c.style += ' text-align:' + config.align + ';';
9493             }
9494             
9495             /* width is done in CSS
9496              *if(typeof(config.width) != 'undefined'){
9497                 c.style += ' width:' + config.width + 'px;';
9498                 this.totalWidth += config.width;
9499             } else {
9500                 this.totalWidth += 100; // assume minimum of 100 per column?
9501             }
9502             */
9503             
9504             if(typeof(config.cls) != 'undefined'){
9505                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9506             }
9507             // this is the bit that doesnt reall work at all...
9508             
9509             if (this.responsive) {
9510                  
9511             
9512                 ['xs','sm','md','lg'].map(function(size){
9513                     
9514                     if(typeof(config[size]) == 'undefined'){
9515                         return;
9516                     }
9517                      
9518                     if (!config[size]) { // 0 = hidden
9519                         // BS 4 '0' is treated as hide that column and below.
9520                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9521                         return;
9522                     }
9523                     
9524                     c.cls += ' col-' + size + '-' + config[size] + (
9525                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9526                     );
9527                     
9528                     
9529                 });
9530             }
9531             // at the end?
9532             
9533             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9534             
9535             
9536             
9537             
9538             header.cn.push(c)
9539         }
9540         
9541         return header;
9542     },
9543     
9544     renderBody : function()
9545     {
9546         var body = {
9547             tag: 'tbody',
9548             cn : [
9549                 {
9550                     tag: 'tr',
9551                     cn : [
9552                         {
9553                             tag : 'td',
9554                             colspan :  this.cm.getColumnCount()
9555                         }
9556                     ]
9557                 }
9558             ]
9559         };
9560         
9561         return body;
9562     },
9563     
9564     renderFooter : function()
9565     {
9566         var footer = {
9567             tag: 'tfoot',
9568             cn : [
9569                 {
9570                     tag: 'tr',
9571                     cn : [
9572                         {
9573                             tag : 'td',
9574                             colspan :  this.cm.getColumnCount()
9575                         }
9576                     ]
9577                 }
9578             ]
9579         };
9580         
9581         return footer;
9582     },
9583     
9584     
9585     
9586     onLoad : function()
9587     {
9588 //        Roo.log('ds onload');
9589         this.clear();
9590         
9591         var _this = this;
9592         var cm = this.cm;
9593         var ds = this.store;
9594         
9595         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9596             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9597             if (_this.store.sortInfo) {
9598                     
9599                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9600                     e.select('i', true).addClass(['fa-arrow-up']);
9601                 }
9602                 
9603                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9604                     e.select('i', true).addClass(['fa-arrow-down']);
9605                 }
9606             }
9607         });
9608         
9609         var tbody =  this.bodyEl;
9610               
9611         if(ds.getCount() > 0){
9612             ds.data.each(function(d,rowIndex){
9613                 var row =  this.renderRow(cm, ds, rowIndex);
9614                 
9615                 tbody.createChild(row);
9616                 
9617                 var _this = this;
9618                 
9619                 if(row.cellObjects.length){
9620                     Roo.each(row.cellObjects, function(r){
9621                         _this.renderCellObject(r);
9622                     })
9623                 }
9624                 
9625             }, this);
9626         }
9627         
9628         var tfoot = this.el.select('tfoot', true).first();
9629         
9630         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9631             
9632             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9633             
9634             var total = this.ds.getTotalCount();
9635             
9636             if(this.footer.pageSize < total){
9637                 this.mainFoot.show();
9638             }
9639         }
9640         
9641         Roo.each(this.el.select('tbody td', true).elements, function(e){
9642             e.on('mouseover', _this.onMouseover, _this);
9643         });
9644         
9645         Roo.each(this.el.select('tbody td', true).elements, function(e){
9646             e.on('mouseout', _this.onMouseout, _this);
9647         });
9648         this.fireEvent('rowsrendered', this);
9649         
9650         this.autoSize();
9651         
9652         this.initCSS(); /// resize cols
9653
9654         
9655     },
9656     
9657     
9658     onUpdate : function(ds,record)
9659     {
9660         this.refreshRow(record);
9661         this.autoSize();
9662     },
9663     
9664     onRemove : function(ds, record, index, isUpdate){
9665         if(isUpdate !== true){
9666             this.fireEvent("beforerowremoved", this, index, record);
9667         }
9668         var bt = this.bodyEl.dom;
9669         
9670         var rows = this.el.select('tbody > tr', true).elements;
9671         
9672         if(typeof(rows[index]) != 'undefined'){
9673             bt.removeChild(rows[index].dom);
9674         }
9675         
9676 //        if(bt.rows[index]){
9677 //            bt.removeChild(bt.rows[index]);
9678 //        }
9679         
9680         if(isUpdate !== true){
9681             //this.stripeRows(index);
9682             //this.syncRowHeights(index, index);
9683             //this.layout();
9684             this.fireEvent("rowremoved", this, index, record);
9685         }
9686     },
9687     
9688     onAdd : function(ds, records, rowIndex)
9689     {
9690         //Roo.log('on Add called');
9691         // - note this does not handle multiple adding very well..
9692         var bt = this.bodyEl.dom;
9693         for (var i =0 ; i < records.length;i++) {
9694             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9695             //Roo.log(records[i]);
9696             //Roo.log(this.store.getAt(rowIndex+i));
9697             this.insertRow(this.store, rowIndex + i, false);
9698             return;
9699         }
9700         
9701     },
9702     
9703     
9704     refreshRow : function(record){
9705         var ds = this.store, index;
9706         if(typeof record == 'number'){
9707             index = record;
9708             record = ds.getAt(index);
9709         }else{
9710             index = ds.indexOf(record);
9711             if (index < 0) {
9712                 return; // should not happen - but seems to 
9713             }
9714         }
9715         this.insertRow(ds, index, true);
9716         this.autoSize();
9717         this.onRemove(ds, record, index+1, true);
9718         this.autoSize();
9719         //this.syncRowHeights(index, index);
9720         //this.layout();
9721         this.fireEvent("rowupdated", this, index, record);
9722     },
9723     // private - called by RowSelection
9724     onRowSelect : function(rowIndex){
9725         var row = this.getRowDom(rowIndex);
9726         row.addClass(['bg-info','info']);
9727     },
9728     // private - called by RowSelection
9729     onRowDeselect : function(rowIndex)
9730     {
9731         if (rowIndex < 0) {
9732             return;
9733         }
9734         var row = this.getRowDom(rowIndex);
9735         row.removeClass(['bg-info','info']);
9736     },
9737       /**
9738      * Focuses the specified row.
9739      * @param {Number} row The row index
9740      */
9741     focusRow : function(row)
9742     {
9743         //Roo.log('GridView.focusRow');
9744         var x = this.bodyEl.dom.scrollLeft;
9745         this.focusCell(row, 0, false);
9746         this.bodyEl.dom.scrollLeft = x;
9747
9748     },
9749      /**
9750      * Focuses the specified cell.
9751      * @param {Number} row The row index
9752      * @param {Number} col The column index
9753      * @param {Boolean} hscroll false to disable horizontal scrolling
9754      */
9755     focusCell : function(row, col, hscroll)
9756     {
9757         //Roo.log('GridView.focusCell');
9758         var el = this.ensureVisible(row, col, hscroll);
9759         // not sure what focusEL achives = it's a <a> pos relative 
9760         //this.focusEl.alignTo(el, "tl-tl");
9761         //if(Roo.isGecko){
9762         //    this.focusEl.focus();
9763         //}else{
9764         //    this.focusEl.focus.defer(1, this.focusEl);
9765         //}
9766     },
9767     
9768      /**
9769      * Scrolls the specified cell into view
9770      * @param {Number} row The row index
9771      * @param {Number} col The column index
9772      * @param {Boolean} hscroll false to disable horizontal scrolling
9773      */
9774     ensureVisible : function(row, col, hscroll)
9775     {
9776         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9777         //return null; //disable for testing.
9778         if(typeof row != "number"){
9779             row = row.rowIndex;
9780         }
9781         if(row < 0 && row >= this.ds.getCount()){
9782             return  null;
9783         }
9784         col = (col !== undefined ? col : 0);
9785         var cm = this.cm;
9786         while(cm.isHidden(col)){
9787             col++;
9788         }
9789
9790         var el = this.getCellDom(row, col);
9791         if(!el){
9792             return null;
9793         }
9794         var c = this.bodyEl.dom;
9795
9796         var ctop = parseInt(el.offsetTop, 10);
9797         var cleft = parseInt(el.offsetLeft, 10);
9798         var cbot = ctop + el.offsetHeight;
9799         var cright = cleft + el.offsetWidth;
9800
9801         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9802         var ch = 0; //?? header is not withing the area?
9803         var stop = parseInt(c.scrollTop, 10);
9804         var sleft = parseInt(c.scrollLeft, 10);
9805         var sbot = stop + ch;
9806         var sright = sleft + c.clientWidth;
9807         /*
9808         Roo.log('GridView.ensureVisible:' +
9809                 ' ctop:' + ctop +
9810                 ' c.clientHeight:' + c.clientHeight +
9811                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9812                 ' stop:' + stop +
9813                 ' cbot:' + cbot +
9814                 ' sbot:' + sbot +
9815                 ' ch:' + ch  
9816                 );
9817         */
9818         if(ctop < stop){
9819             c.scrollTop = ctop;
9820             //Roo.log("set scrolltop to ctop DISABLE?");
9821         }else if(cbot > sbot){
9822             //Roo.log("set scrolltop to cbot-ch");
9823             c.scrollTop = cbot-ch;
9824         }
9825
9826         if(hscroll !== false){
9827             if(cleft < sleft){
9828                 c.scrollLeft = cleft;
9829             }else if(cright > sright){
9830                 c.scrollLeft = cright-c.clientWidth;
9831             }
9832         }
9833
9834         return el;
9835     },
9836     
9837     
9838     insertRow : function(dm, rowIndex, isUpdate){
9839         
9840         if(!isUpdate){
9841             this.fireEvent("beforerowsinserted", this, rowIndex);
9842         }
9843             //var s = this.getScrollState();
9844         var row = this.renderRow(this.cm, this.store, rowIndex);
9845         // insert before rowIndex..
9846         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9847         
9848         var _this = this;
9849                 
9850         if(row.cellObjects.length){
9851             Roo.each(row.cellObjects, function(r){
9852                 _this.renderCellObject(r);
9853             })
9854         }
9855             
9856         if(!isUpdate){
9857             this.fireEvent("rowsinserted", this, rowIndex);
9858             //this.syncRowHeights(firstRow, lastRow);
9859             //this.stripeRows(firstRow);
9860             //this.layout();
9861         }
9862         
9863     },
9864     
9865     
9866     getRowDom : function(rowIndex)
9867     {
9868         var rows = this.el.select('tbody > tr', true).elements;
9869         
9870         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9871         
9872     },
9873     getCellDom : function(rowIndex, colIndex)
9874     {
9875         var row = this.getRowDom(rowIndex);
9876         if (row === false) {
9877             return false;
9878         }
9879         var cols = row.select('td', true).elements;
9880         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9881         
9882     },
9883     
9884     // returns the object tree for a tr..
9885   
9886     
9887     renderRow : function(cm, ds, rowIndex) 
9888     {
9889         var d = ds.getAt(rowIndex);
9890         
9891         var row = {
9892             tag : 'tr',
9893             cls : 'x-row-' + rowIndex,
9894             cn : []
9895         };
9896             
9897         var cellObjects = [];
9898         
9899         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9900             var config = cm.config[i];
9901             
9902             var renderer = cm.getRenderer(i);
9903             var value = '';
9904             var id = false;
9905             
9906             if(typeof(renderer) !== 'undefined'){
9907                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9908             }
9909             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9910             // and are rendered into the cells after the row is rendered - using the id for the element.
9911             
9912             if(typeof(value) === 'object'){
9913                 id = Roo.id();
9914                 cellObjects.push({
9915                     container : id,
9916                     cfg : value 
9917                 })
9918             }
9919             
9920             var rowcfg = {
9921                 record: d,
9922                 rowIndex : rowIndex,
9923                 colIndex : i,
9924                 rowClass : ''
9925             };
9926
9927             this.fireEvent('rowclass', this, rowcfg);
9928             
9929             var td = {
9930                 tag: 'td',
9931                 // this might end up displaying HTML?
9932                 // this is too messy... - better to only do it on columsn you know are going to be too long
9933                 //tooltip : (typeof(value) === 'object') ? '' : value,
9934                 cls : rowcfg.rowClass + ' x-col-' + i,
9935                 style: '',
9936                 html: (typeof(value) === 'object') ? '' : value
9937             };
9938             
9939             if (id) {
9940                 td.id = id;
9941             }
9942             
9943             if(typeof(config.colspan) != 'undefined'){
9944                 td.colspan = config.colspan;
9945             }
9946             
9947             
9948             
9949             if(typeof(config.align) != 'undefined' && config.align.length){
9950                 td.style += ' text-align:' + config.align + ';';
9951             }
9952             if(typeof(config.valign) != 'undefined' && config.valign.length){
9953                 td.style += ' vertical-align:' + config.valign + ';';
9954             }
9955             /*
9956             if(typeof(config.width) != 'undefined'){
9957                 td.style += ' width:' +  config.width + 'px;';
9958             }
9959             */
9960             
9961             if(typeof(config.cursor) != 'undefined'){
9962                 td.style += ' cursor:' +  config.cursor + ';';
9963             }
9964             
9965             if(typeof(config.cls) != 'undefined'){
9966                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9967             }
9968             if (this.responsive) {
9969                 ['xs','sm','md','lg'].map(function(size){
9970                     
9971                     if(typeof(config[size]) == 'undefined'){
9972                         return;
9973                     }
9974                     
9975                     
9976                       
9977                     if (!config[size]) { // 0 = hidden
9978                         // BS 4 '0' is treated as hide that column and below.
9979                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9980                         return;
9981                     }
9982                     
9983                     td.cls += ' col-' + size + '-' + config[size] + (
9984                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9985                     );
9986                      
9987     
9988                 });
9989             }
9990             row.cn.push(td);
9991            
9992         }
9993         
9994         row.cellObjects = cellObjects;
9995         
9996         return row;
9997           
9998     },
9999     
10000     
10001     
10002     onBeforeLoad : function()
10003     {
10004         
10005     },
10006      /**
10007      * Remove all rows
10008      */
10009     clear : function()
10010     {
10011         this.el.select('tbody', true).first().dom.innerHTML = '';
10012     },
10013     /**
10014      * Show or hide a row.
10015      * @param {Number} rowIndex to show or hide
10016      * @param {Boolean} state hide
10017      */
10018     setRowVisibility : function(rowIndex, state)
10019     {
10020         var bt = this.bodyEl.dom;
10021         
10022         var rows = this.el.select('tbody > tr', true).elements;
10023         
10024         if(typeof(rows[rowIndex]) == 'undefined'){
10025             return;
10026         }
10027         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10028         
10029     },
10030     
10031     
10032     getSelectionModel : function(){
10033         if(!this.selModel){
10034             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10035         }
10036         return this.selModel;
10037     },
10038     /*
10039      * Render the Roo.bootstrap object from renderder
10040      */
10041     renderCellObject : function(r)
10042     {
10043         var _this = this;
10044         
10045         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10046         
10047         var t = r.cfg.render(r.container);
10048         
10049         if(r.cfg.cn){
10050             Roo.each(r.cfg.cn, function(c){
10051                 var child = {
10052                     container: t.getChildContainer(),
10053                     cfg: c
10054                 };
10055                 _this.renderCellObject(child);
10056             })
10057         }
10058     },
10059     /**
10060      * get the Row Index from a dom element.
10061      * @param {Roo.Element} row The row to look for
10062      * @returns {Number} the row
10063      */
10064     getRowIndex : function(row)
10065     {
10066         var rowIndex = -1;
10067         
10068         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10069             if(el != row){
10070                 return;
10071             }
10072             
10073             rowIndex = index;
10074         });
10075         
10076         return rowIndex;
10077     },
10078     /**
10079      * get the header TH element for columnIndex
10080      * @param {Number} columnIndex
10081      * @returns {Roo.Element}
10082      */
10083     getHeaderIndex: function(colIndex)
10084     {
10085         var cols = this.headEl.select('th', true).elements;
10086         return cols[colIndex]; 
10087     },
10088     /**
10089      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10090      * @param {domElement} cell to look for
10091      * @returns {Number} the column
10092      */
10093     getCellIndex : function(cell)
10094     {
10095         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10096         if(id){
10097             return parseInt(id[1], 10);
10098         }
10099         return 0;
10100     },
10101      /**
10102      * Returns the grid's underlying element = used by panel.Grid
10103      * @return {Element} The element
10104      */
10105     getGridEl : function(){
10106         return this.el;
10107     },
10108      /**
10109      * Forces a resize - used by panel.Grid
10110      * @return {Element} The element
10111      */
10112     autoSize : function()
10113     {
10114         //var ctr = Roo.get(this.container.dom.parentElement);
10115         var ctr = Roo.get(this.el.dom);
10116         
10117         var thd = this.getGridEl().select('thead',true).first();
10118         var tbd = this.getGridEl().select('tbody', true).first();
10119         var tfd = this.getGridEl().select('tfoot', true).first();
10120         
10121         var cw = ctr.getWidth();
10122         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10123         
10124         if (tbd) {
10125             
10126             tbd.setWidth(ctr.getWidth());
10127             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10128             // this needs fixing for various usage - currently only hydra job advers I think..
10129             //tdb.setHeight(
10130             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10131             //); 
10132             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10133             cw -= barsize;
10134         }
10135         cw = Math.max(cw, this.totalWidth);
10136         this.getGridEl().select('tbody tr',true).setWidth(cw);
10137         this.initCSS();
10138         
10139         // resize 'expandable coloumn?
10140         
10141         return; // we doe not have a view in this design..
10142         
10143     },
10144     onBodyScroll: function()
10145     {
10146         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10147         if(this.headEl){
10148             this.headEl.setStyle({
10149                 'position' : 'relative',
10150                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10151             });
10152         }
10153         
10154         if(this.lazyLoad){
10155             
10156             var scrollHeight = this.bodyEl.dom.scrollHeight;
10157             
10158             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10159             
10160             var height = this.bodyEl.getHeight();
10161             
10162             if(scrollHeight - height == scrollTop) {
10163                 
10164                 var total = this.ds.getTotalCount();
10165                 
10166                 if(this.footer.cursor + this.footer.pageSize < total){
10167                     
10168                     this.footer.ds.load({
10169                         params : {
10170                             start : this.footer.cursor + this.footer.pageSize,
10171                             limit : this.footer.pageSize
10172                         },
10173                         add : true
10174                     });
10175                 }
10176             }
10177             
10178         }
10179     },
10180     onColumnSplitterMoved : function(i, diff)
10181     {
10182         this.userResized = true;
10183         
10184         var cm = this.colModel;
10185         
10186         var w = this.getHeaderIndex(i).getWidth() + diff;
10187         
10188         
10189         cm.setColumnWidth(i, w, true);
10190         this.initCSS();
10191         //var cid = cm.getColumnId(i); << not used in this version?
10192        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10193         
10194         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10195         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10196         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10197 */
10198         //this.updateSplitters();
10199         //this.layout(); << ??
10200         this.fireEvent("columnresize", i, w);
10201     },
10202     onHeaderChange : function()
10203     {
10204         var header = this.renderHeader();
10205         var table = this.el.select('table', true).first();
10206         
10207         this.headEl.remove();
10208         this.headEl = table.createChild(header, this.bodyEl, false);
10209         
10210         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10211             e.on('click', this.sort, this);
10212         }, this);
10213         
10214         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10215             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10216         }
10217         
10218     },
10219     
10220     onHiddenChange : function(colModel, colIndex, hidden)
10221     {
10222         /*
10223         this.cm.setHidden()
10224         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10225         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10226         
10227         this.CSS.updateRule(thSelector, "display", "");
10228         this.CSS.updateRule(tdSelector, "display", "");
10229         
10230         if(hidden){
10231             this.CSS.updateRule(thSelector, "display", "none");
10232             this.CSS.updateRule(tdSelector, "display", "none");
10233         }
10234         */
10235         // onload calls initCSS()
10236         this.onHeaderChange();
10237         this.onLoad();
10238     },
10239     
10240     setColumnWidth: function(col_index, width)
10241     {
10242         // width = "md-2 xs-2..."
10243         if(!this.colModel.config[col_index]) {
10244             return;
10245         }
10246         
10247         var w = width.split(" ");
10248         
10249         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10250         
10251         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10252         
10253         
10254         for(var j = 0; j < w.length; j++) {
10255             
10256             if(!w[j]) {
10257                 continue;
10258             }
10259             
10260             var size_cls = w[j].split("-");
10261             
10262             if(!Number.isInteger(size_cls[1] * 1)) {
10263                 continue;
10264             }
10265             
10266             if(!this.colModel.config[col_index][size_cls[0]]) {
10267                 continue;
10268             }
10269             
10270             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10271                 continue;
10272             }
10273             
10274             h_row[0].classList.replace(
10275                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10276                 "col-"+size_cls[0]+"-"+size_cls[1]
10277             );
10278             
10279             for(var i = 0; i < rows.length; i++) {
10280                 
10281                 var size_cls = w[j].split("-");
10282                 
10283                 if(!Number.isInteger(size_cls[1] * 1)) {
10284                     continue;
10285                 }
10286                 
10287                 if(!this.colModel.config[col_index][size_cls[0]]) {
10288                     continue;
10289                 }
10290                 
10291                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10292                     continue;
10293                 }
10294                 
10295                 rows[i].classList.replace(
10296                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10297                     "col-"+size_cls[0]+"-"+size_cls[1]
10298                 );
10299             }
10300             
10301             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10302         }
10303     }
10304 });
10305
10306 // currently only used to find the split on drag.. 
10307 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10308
10309 /**
10310  * @depricated
10311 */
10312 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10313 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10314 /*
10315  * - LGPL
10316  *
10317  * table cell
10318  * 
10319  */
10320
10321 /**
10322  * @class Roo.bootstrap.TableCell
10323  * @extends Roo.bootstrap.Component
10324  * @children Roo.bootstrap.Component
10325  * @parent Roo.bootstrap.TableRow
10326  * Bootstrap TableCell class
10327  * 
10328  * @cfg {String} html cell contain text
10329  * @cfg {String} cls cell class
10330  * @cfg {String} tag cell tag (td|th) default td
10331  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10332  * @cfg {String} align Aligns the content in a cell
10333  * @cfg {String} axis Categorizes cells
10334  * @cfg {String} bgcolor Specifies the background color of a cell
10335  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10336  * @cfg {Number} colspan Specifies the number of columns a cell should span
10337  * @cfg {String} headers Specifies one or more header cells a cell is related to
10338  * @cfg {Number} height Sets the height of a cell
10339  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10340  * @cfg {Number} rowspan Sets the number of rows a cell should span
10341  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10342  * @cfg {String} valign Vertical aligns the content in a cell
10343  * @cfg {Number} width Specifies the width of a cell
10344  * 
10345  * @constructor
10346  * Create a new TableCell
10347  * @param {Object} config The config object
10348  */
10349
10350 Roo.bootstrap.TableCell = function(config){
10351     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10352 };
10353
10354 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10355     
10356     html: false,
10357     cls: false,
10358     tag: false,
10359     abbr: false,
10360     align: false,
10361     axis: false,
10362     bgcolor: false,
10363     charoff: false,
10364     colspan: false,
10365     headers: false,
10366     height: false,
10367     nowrap: false,
10368     rowspan: false,
10369     scope: false,
10370     valign: false,
10371     width: false,
10372     
10373     
10374     getAutoCreate : function(){
10375         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10376         
10377         cfg = {
10378             tag: 'td'
10379         };
10380         
10381         if(this.tag){
10382             cfg.tag = this.tag;
10383         }
10384         
10385         if (this.html) {
10386             cfg.html=this.html
10387         }
10388         if (this.cls) {
10389             cfg.cls=this.cls
10390         }
10391         if (this.abbr) {
10392             cfg.abbr=this.abbr
10393         }
10394         if (this.align) {
10395             cfg.align=this.align
10396         }
10397         if (this.axis) {
10398             cfg.axis=this.axis
10399         }
10400         if (this.bgcolor) {
10401             cfg.bgcolor=this.bgcolor
10402         }
10403         if (this.charoff) {
10404             cfg.charoff=this.charoff
10405         }
10406         if (this.colspan) {
10407             cfg.colspan=this.colspan
10408         }
10409         if (this.headers) {
10410             cfg.headers=this.headers
10411         }
10412         if (this.height) {
10413             cfg.height=this.height
10414         }
10415         if (this.nowrap) {
10416             cfg.nowrap=this.nowrap
10417         }
10418         if (this.rowspan) {
10419             cfg.rowspan=this.rowspan
10420         }
10421         if (this.scope) {
10422             cfg.scope=this.scope
10423         }
10424         if (this.valign) {
10425             cfg.valign=this.valign
10426         }
10427         if (this.width) {
10428             cfg.width=this.width
10429         }
10430         
10431         
10432         return cfg;
10433     }
10434    
10435 });
10436
10437  
10438
10439  /*
10440  * - LGPL
10441  *
10442  * table row
10443  * 
10444  */
10445
10446 /**
10447  * @class Roo.bootstrap.TableRow
10448  * @extends Roo.bootstrap.Component
10449  * @children Roo.bootstrap.TableCell
10450  * @parent Roo.bootstrap.TableBody
10451  * Bootstrap TableRow class
10452  * @cfg {String} cls row class
10453  * @cfg {String} align Aligns the content in a table row
10454  * @cfg {String} bgcolor Specifies a background color for a table row
10455  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10456  * @cfg {String} valign Vertical aligns the content in a table row
10457  * 
10458  * @constructor
10459  * Create a new TableRow
10460  * @param {Object} config The config object
10461  */
10462
10463 Roo.bootstrap.TableRow = function(config){
10464     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10465 };
10466
10467 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10468     
10469     cls: false,
10470     align: false,
10471     bgcolor: false,
10472     charoff: false,
10473     valign: false,
10474     
10475     getAutoCreate : function(){
10476         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10477         
10478         cfg = {
10479             tag: 'tr'
10480         };
10481             
10482         if(this.cls){
10483             cfg.cls = this.cls;
10484         }
10485         if(this.align){
10486             cfg.align = this.align;
10487         }
10488         if(this.bgcolor){
10489             cfg.bgcolor = this.bgcolor;
10490         }
10491         if(this.charoff){
10492             cfg.charoff = this.charoff;
10493         }
10494         if(this.valign){
10495             cfg.valign = this.valign;
10496         }
10497         
10498         return cfg;
10499     }
10500    
10501 });
10502
10503  
10504
10505  /*
10506  * - LGPL
10507  *
10508  * table body
10509  * 
10510  */
10511
10512 /**
10513  * @class Roo.bootstrap.TableBody
10514  * @extends Roo.bootstrap.Component
10515  * @children Roo.bootstrap.TableRow
10516  * @parent Roo.bootstrap.Table
10517  * Bootstrap TableBody class
10518  * @cfg {String} cls element class
10519  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10520  * @cfg {String} align Aligns the content inside the element
10521  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10522  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10523  * 
10524  * @constructor
10525  * Create a new TableBody
10526  * @param {Object} config The config object
10527  */
10528
10529 Roo.bootstrap.TableBody = function(config){
10530     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10531 };
10532
10533 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10534     
10535     cls: false,
10536     tag: false,
10537     align: false,
10538     charoff: false,
10539     valign: false,
10540     
10541     getAutoCreate : function(){
10542         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10543         
10544         cfg = {
10545             tag: 'tbody'
10546         };
10547             
10548         if (this.cls) {
10549             cfg.cls=this.cls
10550         }
10551         if(this.tag){
10552             cfg.tag = this.tag;
10553         }
10554         
10555         if(this.align){
10556             cfg.align = this.align;
10557         }
10558         if(this.charoff){
10559             cfg.charoff = this.charoff;
10560         }
10561         if(this.valign){
10562             cfg.valign = this.valign;
10563         }
10564         
10565         return cfg;
10566     }
10567     
10568     
10569 //    initEvents : function()
10570 //    {
10571 //        
10572 //        if(!this.store){
10573 //            return;
10574 //        }
10575 //        
10576 //        this.store = Roo.factory(this.store, Roo.data);
10577 //        this.store.on('load', this.onLoad, this);
10578 //        
10579 //        this.store.load();
10580 //        
10581 //    },
10582 //    
10583 //    onLoad: function () 
10584 //    {   
10585 //        this.fireEvent('load', this);
10586 //    }
10587 //    
10588 //   
10589 });
10590
10591  
10592
10593  /*
10594  * Based on:
10595  * Ext JS Library 1.1.1
10596  * Copyright(c) 2006-2007, Ext JS, LLC.
10597  *
10598  * Originally Released Under LGPL - original licence link has changed is not relivant.
10599  *
10600  * Fork - LGPL
10601  * <script type="text/javascript">
10602  */
10603
10604 // as we use this in bootstrap.
10605 Roo.namespace('Roo.form');
10606  /**
10607  * @class Roo.form.Action
10608  * Internal Class used to handle form actions
10609  * @constructor
10610  * @param {Roo.form.BasicForm} el The form element or its id
10611  * @param {Object} config Configuration options
10612  */
10613
10614  
10615  
10616 // define the action interface
10617 Roo.form.Action = function(form, options){
10618     this.form = form;
10619     this.options = options || {};
10620 };
10621 /**
10622  * Client Validation Failed
10623  * @const 
10624  */
10625 Roo.form.Action.CLIENT_INVALID = 'client';
10626 /**
10627  * Server Validation Failed
10628  * @const 
10629  */
10630 Roo.form.Action.SERVER_INVALID = 'server';
10631  /**
10632  * Connect to Server Failed
10633  * @const 
10634  */
10635 Roo.form.Action.CONNECT_FAILURE = 'connect';
10636 /**
10637  * Reading Data from Server Failed
10638  * @const 
10639  */
10640 Roo.form.Action.LOAD_FAILURE = 'load';
10641
10642 Roo.form.Action.prototype = {
10643     type : 'default',
10644     failureType : undefined,
10645     response : undefined,
10646     result : undefined,
10647
10648     // interface method
10649     run : function(options){
10650
10651     },
10652
10653     // interface method
10654     success : function(response){
10655
10656     },
10657
10658     // interface method
10659     handleResponse : function(response){
10660
10661     },
10662
10663     // default connection failure
10664     failure : function(response){
10665         
10666         this.response = response;
10667         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10668         this.form.afterAction(this, false);
10669     },
10670
10671     processResponse : function(response){
10672         this.response = response;
10673         if(!response.responseText){
10674             return true;
10675         }
10676         this.result = this.handleResponse(response);
10677         return this.result;
10678     },
10679
10680     // utility functions used internally
10681     getUrl : function(appendParams){
10682         var url = this.options.url || this.form.url || this.form.el.dom.action;
10683         if(appendParams){
10684             var p = this.getParams();
10685             if(p){
10686                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10687             }
10688         }
10689         return url;
10690     },
10691
10692     getMethod : function(){
10693         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10694     },
10695
10696     getParams : function(){
10697         var bp = this.form.baseParams;
10698         var p = this.options.params;
10699         if(p){
10700             if(typeof p == "object"){
10701                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10702             }else if(typeof p == 'string' && bp){
10703                 p += '&' + Roo.urlEncode(bp);
10704             }
10705         }else if(bp){
10706             p = Roo.urlEncode(bp);
10707         }
10708         return p;
10709     },
10710
10711     createCallback : function(){
10712         return {
10713             success: this.success,
10714             failure: this.failure,
10715             scope: this,
10716             timeout: (this.form.timeout*1000),
10717             upload: this.form.fileUpload ? this.success : undefined
10718         };
10719     }
10720 };
10721
10722 Roo.form.Action.Submit = function(form, options){
10723     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10724 };
10725
10726 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10727     type : 'submit',
10728
10729     haveProgress : false,
10730     uploadComplete : false,
10731     
10732     // uploadProgress indicator.
10733     uploadProgress : function()
10734     {
10735         if (!this.form.progressUrl) {
10736             return;
10737         }
10738         
10739         if (!this.haveProgress) {
10740             Roo.MessageBox.progress("Uploading", "Uploading");
10741         }
10742         if (this.uploadComplete) {
10743            Roo.MessageBox.hide();
10744            return;
10745         }
10746         
10747         this.haveProgress = true;
10748    
10749         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10750         
10751         var c = new Roo.data.Connection();
10752         c.request({
10753             url : this.form.progressUrl,
10754             params: {
10755                 id : uid
10756             },
10757             method: 'GET',
10758             success : function(req){
10759                //console.log(data);
10760                 var rdata = false;
10761                 var edata;
10762                 try  {
10763                    rdata = Roo.decode(req.responseText)
10764                 } catch (e) {
10765                     Roo.log("Invalid data from server..");
10766                     Roo.log(edata);
10767                     return;
10768                 }
10769                 if (!rdata || !rdata.success) {
10770                     Roo.log(rdata);
10771                     Roo.MessageBox.alert(Roo.encode(rdata));
10772                     return;
10773                 }
10774                 var data = rdata.data;
10775                 
10776                 if (this.uploadComplete) {
10777                    Roo.MessageBox.hide();
10778                    return;
10779                 }
10780                    
10781                 if (data){
10782                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10783                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10784                     );
10785                 }
10786                 this.uploadProgress.defer(2000,this);
10787             },
10788        
10789             failure: function(data) {
10790                 Roo.log('progress url failed ');
10791                 Roo.log(data);
10792             },
10793             scope : this
10794         });
10795            
10796     },
10797     
10798     
10799     run : function()
10800     {
10801         // run get Values on the form, so it syncs any secondary forms.
10802         this.form.getValues();
10803         
10804         var o = this.options;
10805         var method = this.getMethod();
10806         var isPost = method == 'POST';
10807         if(o.clientValidation === false || this.form.isValid()){
10808             
10809             if (this.form.progressUrl) {
10810                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10811                     (new Date() * 1) + '' + Math.random());
10812                     
10813             } 
10814             
10815             
10816             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10817                 form:this.form.el.dom,
10818                 url:this.getUrl(!isPost),
10819                 method: method,
10820                 params:isPost ? this.getParams() : null,
10821                 isUpload: this.form.fileUpload,
10822                 formData : this.form.formData
10823             }));
10824             
10825             this.uploadProgress();
10826
10827         }else if (o.clientValidation !== false){ // client validation failed
10828             this.failureType = Roo.form.Action.CLIENT_INVALID;
10829             this.form.afterAction(this, false);
10830         }
10831     },
10832
10833     success : function(response)
10834     {
10835         this.uploadComplete= true;
10836         if (this.haveProgress) {
10837             Roo.MessageBox.hide();
10838         }
10839         
10840         
10841         var result = this.processResponse(response);
10842         if(result === true || result.success){
10843             this.form.afterAction(this, true);
10844             return;
10845         }
10846         if(result.errors){
10847             this.form.markInvalid(result.errors);
10848             this.failureType = Roo.form.Action.SERVER_INVALID;
10849         }
10850         this.form.afterAction(this, false);
10851     },
10852     failure : function(response)
10853     {
10854         this.uploadComplete= true;
10855         if (this.haveProgress) {
10856             Roo.MessageBox.hide();
10857         }
10858         
10859         this.response = response;
10860         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10861         this.form.afterAction(this, false);
10862     },
10863     
10864     handleResponse : function(response){
10865         if(this.form.errorReader){
10866             var rs = this.form.errorReader.read(response);
10867             var errors = [];
10868             if(rs.records){
10869                 for(var i = 0, len = rs.records.length; i < len; i++) {
10870                     var r = rs.records[i];
10871                     errors[i] = r.data;
10872                 }
10873             }
10874             if(errors.length < 1){
10875                 errors = null;
10876             }
10877             return {
10878                 success : rs.success,
10879                 errors : errors
10880             };
10881         }
10882         var ret = false;
10883         try {
10884             ret = Roo.decode(response.responseText);
10885         } catch (e) {
10886             ret = {
10887                 success: false,
10888                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10889                 errors : []
10890             };
10891         }
10892         return ret;
10893         
10894     }
10895 });
10896
10897
10898 Roo.form.Action.Load = function(form, options){
10899     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10900     this.reader = this.form.reader;
10901 };
10902
10903 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10904     type : 'load',
10905
10906     run : function(){
10907         
10908         Roo.Ajax.request(Roo.apply(
10909                 this.createCallback(), {
10910                     method:this.getMethod(),
10911                     url:this.getUrl(false),
10912                     params:this.getParams()
10913         }));
10914     },
10915
10916     success : function(response){
10917         
10918         var result = this.processResponse(response);
10919         if(result === true || !result.success || !result.data){
10920             this.failureType = Roo.form.Action.LOAD_FAILURE;
10921             this.form.afterAction(this, false);
10922             return;
10923         }
10924         this.form.clearInvalid();
10925         this.form.setValues(result.data);
10926         this.form.afterAction(this, true);
10927     },
10928
10929     handleResponse : function(response){
10930         if(this.form.reader){
10931             var rs = this.form.reader.read(response);
10932             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10933             return {
10934                 success : rs.success,
10935                 data : data
10936             };
10937         }
10938         return Roo.decode(response.responseText);
10939     }
10940 });
10941
10942 Roo.form.Action.ACTION_TYPES = {
10943     'load' : Roo.form.Action.Load,
10944     'submit' : Roo.form.Action.Submit
10945 };/*
10946  * - LGPL
10947  *
10948  * form
10949  *
10950  */
10951
10952 /**
10953  * @class Roo.bootstrap.Form
10954  * @extends Roo.bootstrap.Component
10955  * @children Roo.bootstrap.Component
10956  * Bootstrap Form class
10957  * @cfg {String} method  GET | POST (default POST)
10958  * @cfg {String} labelAlign top | left (default top)
10959  * @cfg {String} align left  | right - for navbars
10960  * @cfg {Boolean} loadMask load mask when submit (default true)
10961
10962  *
10963  * @constructor
10964  * Create a new Form
10965  * @param {Object} config The config object
10966  */
10967
10968
10969 Roo.bootstrap.Form = function(config){
10970     
10971     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10972     
10973     Roo.bootstrap.Form.popover.apply();
10974     
10975     this.addEvents({
10976         /**
10977          * @event clientvalidation
10978          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10979          * @param {Form} this
10980          * @param {Boolean} valid true if the form has passed client-side validation
10981          */
10982         clientvalidation: true,
10983         /**
10984          * @event beforeaction
10985          * Fires before any action is performed. Return false to cancel the action.
10986          * @param {Form} this
10987          * @param {Action} action The action to be performed
10988          */
10989         beforeaction: true,
10990         /**
10991          * @event actionfailed
10992          * Fires when an action fails.
10993          * @param {Form} this
10994          * @param {Action} action The action that failed
10995          */
10996         actionfailed : true,
10997         /**
10998          * @event actioncomplete
10999          * Fires when an action is completed.
11000          * @param {Form} this
11001          * @param {Action} action The action that completed
11002          */
11003         actioncomplete : true
11004     });
11005 };
11006
11007 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
11008
11009      /**
11010      * @cfg {String} method
11011      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11012      */
11013     method : 'POST',
11014     /**
11015      * @cfg {String} url
11016      * The URL to use for form actions if one isn't supplied in the action options.
11017      */
11018     /**
11019      * @cfg {Boolean} fileUpload
11020      * Set to true if this form is a file upload.
11021      */
11022
11023     /**
11024      * @cfg {Object} baseParams
11025      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11026      */
11027
11028     /**
11029      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11030      */
11031     timeout: 30,
11032     /**
11033      * @cfg {Sting} align (left|right) for navbar forms
11034      */
11035     align : 'left',
11036
11037     // private
11038     activeAction : null,
11039
11040     /**
11041      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11042      * element by passing it or its id or mask the form itself by passing in true.
11043      * @type Mixed
11044      */
11045     waitMsgTarget : false,
11046
11047     loadMask : true,
11048     
11049     /**
11050      * @cfg {Boolean} errorMask (true|false) default false
11051      */
11052     errorMask : false,
11053     
11054     /**
11055      * @cfg {Number} maskOffset Default 100
11056      */
11057     maskOffset : 100,
11058     
11059     /**
11060      * @cfg {Boolean} maskBody
11061      */
11062     maskBody : false,
11063
11064     getAutoCreate : function(){
11065
11066         var cfg = {
11067             tag: 'form',
11068             method : this.method || 'POST',
11069             id : this.id || Roo.id(),
11070             cls : ''
11071         };
11072         if (this.parent().xtype.match(/^Nav/)) {
11073             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11074
11075         }
11076
11077         if (this.labelAlign == 'left' ) {
11078             cfg.cls += ' form-horizontal';
11079         }
11080
11081
11082         return cfg;
11083     },
11084     initEvents : function()
11085     {
11086         this.el.on('submit', this.onSubmit, this);
11087         // this was added as random key presses on the form where triggering form submit.
11088         this.el.on('keypress', function(e) {
11089             if (e.getCharCode() != 13) {
11090                 return true;
11091             }
11092             // we might need to allow it for textareas.. and some other items.
11093             // check e.getTarget().
11094
11095             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11096                 return true;
11097             }
11098
11099             Roo.log("keypress blocked");
11100
11101             e.preventDefault();
11102             return false;
11103         });
11104         
11105     },
11106     // private
11107     onSubmit : function(e){
11108         e.stopEvent();
11109     },
11110
11111      /**
11112      * Returns true if client-side validation on the form is successful.
11113      * @return Boolean
11114      */
11115     isValid : function(){
11116         var items = this.getItems();
11117         var valid = true;
11118         var target = false;
11119         
11120         items.each(function(f){
11121             
11122             if(f.validate()){
11123                 return;
11124             }
11125             
11126             Roo.log('invalid field: ' + f.name);
11127             
11128             valid = false;
11129
11130             if(!target && f.el.isVisible(true)){
11131                 target = f;
11132             }
11133            
11134         });
11135         
11136         if(this.errorMask && !valid){
11137             Roo.bootstrap.Form.popover.mask(this, target);
11138         }
11139         
11140         return valid;
11141     },
11142     
11143     /**
11144      * Returns true if any fields in this form have changed since their original load.
11145      * @return Boolean
11146      */
11147     isDirty : function(){
11148         var dirty = false;
11149         var items = this.getItems();
11150         items.each(function(f){
11151            if(f.isDirty()){
11152                dirty = true;
11153                return false;
11154            }
11155            return true;
11156         });
11157         return dirty;
11158     },
11159      /**
11160      * Performs a predefined action (submit or load) or custom actions you define on this form.
11161      * @param {String} actionName The name of the action type
11162      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11163      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11164      * accept other config options):
11165      * <pre>
11166 Property          Type             Description
11167 ----------------  ---------------  ----------------------------------------------------------------------------------
11168 url               String           The url for the action (defaults to the form's url)
11169 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11170 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11171 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11172                                    validate the form on the client (defaults to false)
11173      * </pre>
11174      * @return {BasicForm} this
11175      */
11176     doAction : function(action, options){
11177         if(typeof action == 'string'){
11178             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11179         }
11180         if(this.fireEvent('beforeaction', this, action) !== false){
11181             this.beforeAction(action);
11182             action.run.defer(100, action);
11183         }
11184         return this;
11185     },
11186
11187     // private
11188     beforeAction : function(action){
11189         var o = action.options;
11190         
11191         if(this.loadMask){
11192             
11193             if(this.maskBody){
11194                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11195             } else {
11196                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11197             }
11198         }
11199         // not really supported yet.. ??
11200
11201         //if(this.waitMsgTarget === true){
11202         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11203         //}else if(this.waitMsgTarget){
11204         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11205         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11206         //}else {
11207         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11208        // }
11209
11210     },
11211
11212     // private
11213     afterAction : function(action, success){
11214         this.activeAction = null;
11215         var o = action.options;
11216
11217         if(this.loadMask){
11218             
11219             if(this.maskBody){
11220                 Roo.get(document.body).unmask();
11221             } else {
11222                 this.el.unmask();
11223             }
11224         }
11225         
11226         //if(this.waitMsgTarget === true){
11227 //            this.el.unmask();
11228         //}else if(this.waitMsgTarget){
11229         //    this.waitMsgTarget.unmask();
11230         //}else{
11231         //    Roo.MessageBox.updateProgress(1);
11232         //    Roo.MessageBox.hide();
11233        // }
11234         //
11235         if(success){
11236             if(o.reset){
11237                 this.reset();
11238             }
11239             Roo.callback(o.success, o.scope, [this, action]);
11240             this.fireEvent('actioncomplete', this, action);
11241
11242         }else{
11243
11244             // failure condition..
11245             // we have a scenario where updates need confirming.
11246             // eg. if a locking scenario exists..
11247             // we look for { errors : { needs_confirm : true }} in the response.
11248             if (
11249                 (typeof(action.result) != 'undefined')  &&
11250                 (typeof(action.result.errors) != 'undefined')  &&
11251                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11252            ){
11253                 var _t = this;
11254                 Roo.log("not supported yet");
11255                  /*
11256
11257                 Roo.MessageBox.confirm(
11258                     "Change requires confirmation",
11259                     action.result.errorMsg,
11260                     function(r) {
11261                         if (r != 'yes') {
11262                             return;
11263                         }
11264                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11265                     }
11266
11267                 );
11268                 */
11269
11270
11271                 return;
11272             }
11273
11274             Roo.callback(o.failure, o.scope, [this, action]);
11275             // show an error message if no failed handler is set..
11276             if (!this.hasListener('actionfailed')) {
11277                 Roo.log("need to add dialog support");
11278                 /*
11279                 Roo.MessageBox.alert("Error",
11280                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11281                         action.result.errorMsg :
11282                         "Saving Failed, please check your entries or try again"
11283                 );
11284                 */
11285             }
11286
11287             this.fireEvent('actionfailed', this, action);
11288         }
11289
11290     },
11291     /**
11292      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11293      * @param {String} id The value to search for
11294      * @return Field
11295      */
11296     findField : function(id){
11297         var items = this.getItems();
11298         var field = items.get(id);
11299         if(!field){
11300              items.each(function(f){
11301                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11302                     field = f;
11303                     return false;
11304                 }
11305                 return true;
11306             });
11307         }
11308         return field || null;
11309     },
11310      /**
11311      * Mark fields in this form invalid in bulk.
11312      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11313      * @return {BasicForm} this
11314      */
11315     markInvalid : function(errors){
11316         if(errors instanceof Array){
11317             for(var i = 0, len = errors.length; i < len; i++){
11318                 var fieldError = errors[i];
11319                 var f = this.findField(fieldError.id);
11320                 if(f){
11321                     f.markInvalid(fieldError.msg);
11322                 }
11323             }
11324         }else{
11325             var field, id;
11326             for(id in errors){
11327                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11328                     field.markInvalid(errors[id]);
11329                 }
11330             }
11331         }
11332         //Roo.each(this.childForms || [], function (f) {
11333         //    f.markInvalid(errors);
11334         //});
11335
11336         return this;
11337     },
11338
11339     /**
11340      * Set values for fields in this form in bulk.
11341      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11342      * @return {BasicForm} this
11343      */
11344     setValues : function(values){
11345         if(values instanceof Array){ // array of objects
11346             for(var i = 0, len = values.length; i < len; i++){
11347                 var v = values[i];
11348                 var f = this.findField(v.id);
11349                 if(f){
11350                     f.setValue(v.value);
11351                     if(this.trackResetOnLoad){
11352                         f.originalValue = f.getValue();
11353                     }
11354                 }
11355             }
11356         }else{ // object hash
11357             var field, id;
11358             for(id in values){
11359                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11360
11361                     if (field.setFromData &&
11362                         field.valueField &&
11363                         field.displayField &&
11364                         // combos' with local stores can
11365                         // be queried via setValue()
11366                         // to set their value..
11367                         (field.store && !field.store.isLocal)
11368                         ) {
11369                         // it's a combo
11370                         var sd = { };
11371                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11372                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11373                         field.setFromData(sd);
11374
11375                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11376                         
11377                         field.setFromData(values);
11378                         
11379                     } else {
11380                         field.setValue(values[id]);
11381                     }
11382
11383
11384                     if(this.trackResetOnLoad){
11385                         field.originalValue = field.getValue();
11386                     }
11387                 }
11388             }
11389         }
11390
11391         //Roo.each(this.childForms || [], function (f) {
11392         //    f.setValues(values);
11393         //});
11394
11395         return this;
11396     },
11397
11398     /**
11399      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11400      * they are returned as an array.
11401      * @param {Boolean} asString
11402      * @return {Object}
11403      */
11404     getValues : function(asString){
11405         //if (this.childForms) {
11406             // copy values from the child forms
11407         //    Roo.each(this.childForms, function (f) {
11408         //        this.setValues(f.getValues());
11409         //    }, this);
11410         //}
11411
11412
11413
11414         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11415         if(asString === true){
11416             return fs;
11417         }
11418         return Roo.urlDecode(fs);
11419     },
11420
11421     /**
11422      * Returns the fields in this form as an object with key/value pairs.
11423      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11424      * @return {Object}
11425      */
11426     getFieldValues : function(with_hidden)
11427     {
11428         var items = this.getItems();
11429         var ret = {};
11430         items.each(function(f){
11431             
11432             if (!f.getName()) {
11433                 return;
11434             }
11435             
11436             var v = f.getValue();
11437             
11438             if (f.inputType =='radio') {
11439                 if (typeof(ret[f.getName()]) == 'undefined') {
11440                     ret[f.getName()] = ''; // empty..
11441                 }
11442
11443                 if (!f.el.dom.checked) {
11444                     return;
11445
11446                 }
11447                 v = f.el.dom.value;
11448
11449             }
11450             
11451             if(f.xtype == 'MoneyField'){
11452                 ret[f.currencyName] = f.getCurrency();
11453             }
11454
11455             // not sure if this supported any more..
11456             if ((typeof(v) == 'object') && f.getRawValue) {
11457                 v = f.getRawValue() ; // dates..
11458             }
11459             // combo boxes where name != hiddenName...
11460             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11461                 ret[f.name] = f.getRawValue();
11462             }
11463             ret[f.getName()] = v;
11464         });
11465
11466         return ret;
11467     },
11468
11469     /**
11470      * Clears all invalid messages in this form.
11471      * @return {BasicForm} this
11472      */
11473     clearInvalid : function(){
11474         var items = this.getItems();
11475
11476         items.each(function(f){
11477            f.clearInvalid();
11478         });
11479
11480         return this;
11481     },
11482
11483     /**
11484      * Resets this form.
11485      * @return {BasicForm} this
11486      */
11487     reset : function(){
11488         var items = this.getItems();
11489         items.each(function(f){
11490             f.reset();
11491         });
11492
11493         Roo.each(this.childForms || [], function (f) {
11494             f.reset();
11495         });
11496
11497
11498         return this;
11499     },
11500     
11501     getItems : function()
11502     {
11503         var r=new Roo.util.MixedCollection(false, function(o){
11504             return o.id || (o.id = Roo.id());
11505         });
11506         var iter = function(el) {
11507             if (el.inputEl) {
11508                 r.add(el);
11509             }
11510             if (!el.items) {
11511                 return;
11512             }
11513             Roo.each(el.items,function(e) {
11514                 iter(e);
11515             });
11516         };
11517
11518         iter(this);
11519         return r;
11520     },
11521     
11522     hideFields : function(items)
11523     {
11524         Roo.each(items, function(i){
11525             
11526             var f = this.findField(i);
11527             
11528             if(!f){
11529                 return;
11530             }
11531             
11532             f.hide();
11533             
11534         }, this);
11535     },
11536     
11537     showFields : function(items)
11538     {
11539         Roo.each(items, function(i){
11540             
11541             var f = this.findField(i);
11542             
11543             if(!f){
11544                 return;
11545             }
11546             
11547             f.show();
11548             
11549         }, this);
11550     }
11551
11552 });
11553
11554 Roo.apply(Roo.bootstrap.Form, {
11555     
11556     popover : {
11557         
11558         padding : 5,
11559         
11560         isApplied : false,
11561         
11562         isMasked : false,
11563         
11564         form : false,
11565         
11566         target : false,
11567         
11568         toolTip : false,
11569         
11570         intervalID : false,
11571         
11572         maskEl : false,
11573         
11574         apply : function()
11575         {
11576             if(this.isApplied){
11577                 return;
11578             }
11579             
11580             this.maskEl = {
11581                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11582                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11583                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11584                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11585             };
11586             
11587             this.maskEl.top.enableDisplayMode("block");
11588             this.maskEl.left.enableDisplayMode("block");
11589             this.maskEl.bottom.enableDisplayMode("block");
11590             this.maskEl.right.enableDisplayMode("block");
11591             
11592             this.toolTip = new Roo.bootstrap.Tooltip({
11593                 cls : 'roo-form-error-popover',
11594                 alignment : {
11595                     'left' : ['r-l', [-2,0], 'right'],
11596                     'right' : ['l-r', [2,0], 'left'],
11597                     'bottom' : ['tl-bl', [0,2], 'top'],
11598                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11599                 }
11600             });
11601             
11602             this.toolTip.render(Roo.get(document.body));
11603
11604             this.toolTip.el.enableDisplayMode("block");
11605             
11606             Roo.get(document.body).on('click', function(){
11607                 this.unmask();
11608             }, this);
11609             
11610             Roo.get(document.body).on('touchstart', function(){
11611                 this.unmask();
11612             }, this);
11613             
11614             this.isApplied = true
11615         },
11616         
11617         mask : function(form, target)
11618         {
11619             this.form = form;
11620             
11621             this.target = target;
11622             
11623             if(!this.form.errorMask || !target.el){
11624                 return;
11625             }
11626             
11627             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11628             
11629             Roo.log(scrollable);
11630             
11631             var ot = this.target.el.calcOffsetsTo(scrollable);
11632             
11633             var scrollTo = ot[1] - this.form.maskOffset;
11634             
11635             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11636             
11637             scrollable.scrollTo('top', scrollTo);
11638             
11639             var box = this.target.el.getBox();
11640             Roo.log(box);
11641             var zIndex = Roo.bootstrap.Modal.zIndex++;
11642
11643             
11644             this.maskEl.top.setStyle('position', 'absolute');
11645             this.maskEl.top.setStyle('z-index', zIndex);
11646             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11647             this.maskEl.top.setLeft(0);
11648             this.maskEl.top.setTop(0);
11649             this.maskEl.top.show();
11650             
11651             this.maskEl.left.setStyle('position', 'absolute');
11652             this.maskEl.left.setStyle('z-index', zIndex);
11653             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11654             this.maskEl.left.setLeft(0);
11655             this.maskEl.left.setTop(box.y - this.padding);
11656             this.maskEl.left.show();
11657
11658             this.maskEl.bottom.setStyle('position', 'absolute');
11659             this.maskEl.bottom.setStyle('z-index', zIndex);
11660             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11661             this.maskEl.bottom.setLeft(0);
11662             this.maskEl.bottom.setTop(box.bottom + this.padding);
11663             this.maskEl.bottom.show();
11664
11665             this.maskEl.right.setStyle('position', 'absolute');
11666             this.maskEl.right.setStyle('z-index', zIndex);
11667             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11668             this.maskEl.right.setLeft(box.right + this.padding);
11669             this.maskEl.right.setTop(box.y - this.padding);
11670             this.maskEl.right.show();
11671
11672             this.toolTip.bindEl = this.target.el;
11673
11674             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11675
11676             var tip = this.target.blankText;
11677
11678             if(this.target.getValue() !== '' ) {
11679                 
11680                 if (this.target.invalidText.length) {
11681                     tip = this.target.invalidText;
11682                 } else if (this.target.regexText.length){
11683                     tip = this.target.regexText;
11684                 }
11685             }
11686
11687             this.toolTip.show(tip);
11688
11689             this.intervalID = window.setInterval(function() {
11690                 Roo.bootstrap.Form.popover.unmask();
11691             }, 10000);
11692
11693             window.onwheel = function(){ return false;};
11694             
11695             (function(){ this.isMasked = true; }).defer(500, this);
11696             
11697         },
11698         
11699         unmask : function()
11700         {
11701             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11702                 return;
11703             }
11704             
11705             this.maskEl.top.setStyle('position', 'absolute');
11706             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11707             this.maskEl.top.hide();
11708
11709             this.maskEl.left.setStyle('position', 'absolute');
11710             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11711             this.maskEl.left.hide();
11712
11713             this.maskEl.bottom.setStyle('position', 'absolute');
11714             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11715             this.maskEl.bottom.hide();
11716
11717             this.maskEl.right.setStyle('position', 'absolute');
11718             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11719             this.maskEl.right.hide();
11720             
11721             this.toolTip.hide();
11722             
11723             this.toolTip.el.hide();
11724             
11725             window.onwheel = function(){ return true;};
11726             
11727             if(this.intervalID){
11728                 window.clearInterval(this.intervalID);
11729                 this.intervalID = false;
11730             }
11731             
11732             this.isMasked = false;
11733             
11734         }
11735         
11736     }
11737     
11738 });
11739
11740 /*
11741  * Based on:
11742  * Ext JS Library 1.1.1
11743  * Copyright(c) 2006-2007, Ext JS, LLC.
11744  *
11745  * Originally Released Under LGPL - original licence link has changed is not relivant.
11746  *
11747  * Fork - LGPL
11748  * <script type="text/javascript">
11749  */
11750 /**
11751  * @class Roo.form.VTypes
11752  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11753  * @singleton
11754  */
11755 Roo.form.VTypes = function(){
11756     // closure these in so they are only created once.
11757     var alpha = /^[a-zA-Z_]+$/;
11758     var alphanum = /^[a-zA-Z0-9_]+$/;
11759     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11760     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11761
11762     // All these messages and functions are configurable
11763     return {
11764         /**
11765          * The function used to validate email addresses
11766          * @param {String} value The email address
11767          */
11768         'email' : function(v){
11769             return email.test(v);
11770         },
11771         /**
11772          * The error text to display when the email validation function returns false
11773          * @type String
11774          */
11775         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11776         /**
11777          * The keystroke filter mask to be applied on email input
11778          * @type RegExp
11779          */
11780         'emailMask' : /[a-z0-9_\.\-@]/i,
11781
11782         /**
11783          * The function used to validate URLs
11784          * @param {String} value The URL
11785          */
11786         'url' : function(v){
11787             return url.test(v);
11788         },
11789         /**
11790          * The error text to display when the url validation function returns false
11791          * @type String
11792          */
11793         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11794         
11795         /**
11796          * The function used to validate alpha values
11797          * @param {String} value The value
11798          */
11799         'alpha' : function(v){
11800             return alpha.test(v);
11801         },
11802         /**
11803          * The error text to display when the alpha validation function returns false
11804          * @type String
11805          */
11806         'alphaText' : 'This field should only contain letters and _',
11807         /**
11808          * The keystroke filter mask to be applied on alpha input
11809          * @type RegExp
11810          */
11811         'alphaMask' : /[a-z_]/i,
11812
11813         /**
11814          * The function used to validate alphanumeric values
11815          * @param {String} value The value
11816          */
11817         'alphanum' : function(v){
11818             return alphanum.test(v);
11819         },
11820         /**
11821          * The error text to display when the alphanumeric validation function returns false
11822          * @type String
11823          */
11824         'alphanumText' : 'This field should only contain letters, numbers and _',
11825         /**
11826          * The keystroke filter mask to be applied on alphanumeric input
11827          * @type RegExp
11828          */
11829         'alphanumMask' : /[a-z0-9_]/i
11830     };
11831 }();/*
11832  * - LGPL
11833  *
11834  * Input
11835  * 
11836  */
11837
11838 /**
11839  * @class Roo.bootstrap.Input
11840  * @extends Roo.bootstrap.Component
11841  * Bootstrap Input class
11842  * @cfg {Boolean} disabled is it disabled
11843  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11844  * @cfg {String} name name of the input
11845  * @cfg {string} fieldLabel - the label associated
11846  * @cfg {string} placeholder - placeholder to put in text.
11847  * @cfg {string}  before - input group add on before
11848  * @cfg {string} after - input group add on after
11849  * @cfg {string} size - (lg|sm) or leave empty..
11850  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11851  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11852  * @cfg {Number} md colspan out of 12 for computer-sized screens
11853  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11854  * @cfg {string} value default value of the input
11855  * @cfg {Number} labelWidth set the width of label 
11856  * @cfg {Number} labellg set the width of label (1-12)
11857  * @cfg {Number} labelmd set the width of label (1-12)
11858  * @cfg {Number} labelsm set the width of label (1-12)
11859  * @cfg {Number} labelxs set the width of label (1-12)
11860  * @cfg {String} labelAlign (top|left)
11861  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11862  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11863  * @cfg {String} indicatorpos (left|right) default left
11864  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11865  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11866  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11867
11868  * @cfg {String} align (left|center|right) Default left
11869  * @cfg {Boolean} forceFeedback (true|false) Default false
11870  * 
11871  * @constructor
11872  * Create a new Input
11873  * @param {Object} config The config object
11874  */
11875
11876 Roo.bootstrap.Input = function(config){
11877     
11878     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11879     
11880     this.addEvents({
11881         /**
11882          * @event focus
11883          * Fires when this field receives input focus.
11884          * @param {Roo.form.Field} this
11885          */
11886         focus : true,
11887         /**
11888          * @event blur
11889          * Fires when this field loses input focus.
11890          * @param {Roo.form.Field} this
11891          */
11892         blur : true,
11893         /**
11894          * @event specialkey
11895          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11896          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11897          * @param {Roo.form.Field} this
11898          * @param {Roo.EventObject} e The event object
11899          */
11900         specialkey : true,
11901         /**
11902          * @event change
11903          * Fires just before the field blurs if the field value has changed.
11904          * @param {Roo.form.Field} this
11905          * @param {Mixed} newValue The new value
11906          * @param {Mixed} oldValue The original value
11907          */
11908         change : true,
11909         /**
11910          * @event invalid
11911          * Fires after the field has been marked as invalid.
11912          * @param {Roo.form.Field} this
11913          * @param {String} msg The validation message
11914          */
11915         invalid : true,
11916         /**
11917          * @event valid
11918          * Fires after the field has been validated with no errors.
11919          * @param {Roo.form.Field} this
11920          */
11921         valid : true,
11922          /**
11923          * @event keyup
11924          * Fires after the key up
11925          * @param {Roo.form.Field} this
11926          * @param {Roo.EventObject}  e The event Object
11927          */
11928         keyup : true,
11929         /**
11930          * @event paste
11931          * Fires after the user pastes into input
11932          * @param {Roo.form.Field} this
11933          * @param {Roo.EventObject}  e The event Object
11934          */
11935         paste : true
11936     });
11937 };
11938
11939 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11940      /**
11941      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11942       automatic validation (defaults to "keyup").
11943      */
11944     validationEvent : "keyup",
11945      /**
11946      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11947      */
11948     validateOnBlur : true,
11949     /**
11950      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11951      */
11952     validationDelay : 250,
11953      /**
11954      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11955      */
11956     focusClass : "x-form-focus",  // not needed???
11957     
11958        
11959     /**
11960      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11961      */
11962     invalidClass : "has-warning",
11963     
11964     /**
11965      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11966      */
11967     validClass : "has-success",
11968     
11969     /**
11970      * @cfg {Boolean} hasFeedback (true|false) default true
11971      */
11972     hasFeedback : true,
11973     
11974     /**
11975      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11976      */
11977     invalidFeedbackClass : "glyphicon-warning-sign",
11978     
11979     /**
11980      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11981      */
11982     validFeedbackClass : "glyphicon-ok",
11983     
11984     /**
11985      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11986      */
11987     selectOnFocus : false,
11988     
11989      /**
11990      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11991      */
11992     maskRe : null,
11993        /**
11994      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11995      */
11996     vtype : null,
11997     
11998       /**
11999      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12000      */
12001     disableKeyFilter : false,
12002     
12003        /**
12004      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12005      */
12006     disabled : false,
12007      /**
12008      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12009      */
12010     allowBlank : true,
12011     /**
12012      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12013      */
12014     blankText : "Please complete this mandatory field",
12015     
12016      /**
12017      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12018      */
12019     minLength : 0,
12020     /**
12021      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12022      */
12023     maxLength : Number.MAX_VALUE,
12024     /**
12025      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12026      */
12027     minLengthText : "The minimum length for this field is {0}",
12028     /**
12029      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12030      */
12031     maxLengthText : "The maximum length for this field is {0}",
12032   
12033     
12034     /**
12035      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12036      * If available, this function will be called only after the basic validators all return true, and will be passed the
12037      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12038      */
12039     validator : null,
12040     /**
12041      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12042      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12043      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12044      */
12045     regex : null,
12046     /**
12047      * @cfg {String} regexText -- Depricated - use Invalid Text
12048      */
12049     regexText : "",
12050     
12051     /**
12052      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12053      */
12054     invalidText : "",
12055     
12056     
12057     
12058     autocomplete: false,
12059     
12060     
12061     fieldLabel : '',
12062     inputType : 'text',
12063     
12064     name : false,
12065     placeholder: false,
12066     before : false,
12067     after : false,
12068     size : false,
12069     hasFocus : false,
12070     preventMark: false,
12071     isFormField : true,
12072     value : '',
12073     labelWidth : 2,
12074     labelAlign : false,
12075     readOnly : false,
12076     align : false,
12077     formatedValue : false,
12078     forceFeedback : false,
12079     
12080     indicatorpos : 'left',
12081     
12082     labellg : 0,
12083     labelmd : 0,
12084     labelsm : 0,
12085     labelxs : 0,
12086     
12087     capture : '',
12088     accept : '',
12089     
12090     parentLabelAlign : function()
12091     {
12092         var parent = this;
12093         while (parent.parent()) {
12094             parent = parent.parent();
12095             if (typeof(parent.labelAlign) !='undefined') {
12096                 return parent.labelAlign;
12097             }
12098         }
12099         return 'left';
12100         
12101     },
12102     
12103     getAutoCreate : function()
12104     {
12105         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12106         
12107         var id = Roo.id();
12108         
12109         var cfg = {};
12110         
12111         if(this.inputType != 'hidden'){
12112             cfg.cls = 'form-group' //input-group
12113         }
12114         
12115         var input =  {
12116             tag: 'input',
12117             id : id,
12118             type : this.inputType,
12119             value : this.value,
12120             cls : 'form-control',
12121             placeholder : this.placeholder || '',
12122             autocomplete : this.autocomplete || 'new-password'
12123         };
12124         if (this.inputType == 'file') {
12125             input.style = 'overflow:hidden'; // why not in CSS?
12126         }
12127         
12128         if(this.capture.length){
12129             input.capture = this.capture;
12130         }
12131         
12132         if(this.accept.length){
12133             input.accept = this.accept + "/*";
12134         }
12135         
12136         if(this.align){
12137             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12138         }
12139         
12140         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12141             input.maxLength = this.maxLength;
12142         }
12143         
12144         if (this.disabled) {
12145             input.disabled=true;
12146         }
12147         
12148         if (this.readOnly) {
12149             input.readonly=true;
12150         }
12151         
12152         if (this.name) {
12153             input.name = this.name;
12154         }
12155         
12156         if (this.size) {
12157             input.cls += ' input-' + this.size;
12158         }
12159         
12160         var settings=this;
12161         ['xs','sm','md','lg'].map(function(size){
12162             if (settings[size]) {
12163                 cfg.cls += ' col-' + size + '-' + settings[size];
12164             }
12165         });
12166         
12167         var inputblock = input;
12168         
12169         var feedback = {
12170             tag: 'span',
12171             cls: 'glyphicon form-control-feedback'
12172         };
12173             
12174         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12175             
12176             inputblock = {
12177                 cls : 'has-feedback',
12178                 cn :  [
12179                     input,
12180                     feedback
12181                 ] 
12182             };  
12183         }
12184         
12185         if (this.before || this.after) {
12186             
12187             inputblock = {
12188                 cls : 'input-group',
12189                 cn :  [] 
12190             };
12191             
12192             if (this.before && typeof(this.before) == 'string') {
12193                 
12194                 inputblock.cn.push({
12195                     tag :'span',
12196                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12197                     html : this.before
12198                 });
12199             }
12200             if (this.before && typeof(this.before) == 'object') {
12201                 this.before = Roo.factory(this.before);
12202                 
12203                 inputblock.cn.push({
12204                     tag :'span',
12205                     cls : 'roo-input-before input-group-prepend   input-group-' +
12206                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12207                 });
12208             }
12209             
12210             inputblock.cn.push(input);
12211             
12212             if (this.after && typeof(this.after) == 'string') {
12213                 inputblock.cn.push({
12214                     tag :'span',
12215                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12216                     html : this.after
12217                 });
12218             }
12219             if (this.after && typeof(this.after) == 'object') {
12220                 this.after = Roo.factory(this.after);
12221                 
12222                 inputblock.cn.push({
12223                     tag :'span',
12224                     cls : 'roo-input-after input-group-append  input-group-' +
12225                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12226                 });
12227             }
12228             
12229             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12230                 inputblock.cls += ' has-feedback';
12231                 inputblock.cn.push(feedback);
12232             }
12233         };
12234         var indicator = {
12235             tag : 'i',
12236             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12237             tooltip : 'This field is required'
12238         };
12239         if (this.allowBlank ) {
12240             indicator.style = this.allowBlank ? ' display:none' : '';
12241         }
12242         if (align ==='left' && this.fieldLabel.length) {
12243             
12244             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12245             
12246             cfg.cn = [
12247                 indicator,
12248                 {
12249                     tag: 'label',
12250                     'for' :  id,
12251                     cls : 'control-label col-form-label',
12252                     html : this.fieldLabel
12253
12254                 },
12255                 {
12256                     cls : "", 
12257                     cn: [
12258                         inputblock
12259                     ]
12260                 }
12261             ];
12262             
12263             var labelCfg = cfg.cn[1];
12264             var contentCfg = cfg.cn[2];
12265             
12266             if(this.indicatorpos == 'right'){
12267                 cfg.cn = [
12268                     {
12269                         tag: 'label',
12270                         'for' :  id,
12271                         cls : 'control-label col-form-label',
12272                         cn : [
12273                             {
12274                                 tag : 'span',
12275                                 html : this.fieldLabel
12276                             },
12277                             indicator
12278                         ]
12279                     },
12280                     {
12281                         cls : "",
12282                         cn: [
12283                             inputblock
12284                         ]
12285                     }
12286
12287                 ];
12288                 
12289                 labelCfg = cfg.cn[0];
12290                 contentCfg = cfg.cn[1];
12291             
12292             }
12293             
12294             if(this.labelWidth > 12){
12295                 labelCfg.style = "width: " + this.labelWidth + 'px';
12296             }
12297             
12298             if(this.labelWidth < 13 && this.labelmd == 0){
12299                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12300             }
12301             
12302             if(this.labellg > 0){
12303                 labelCfg.cls += ' col-lg-' + this.labellg;
12304                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12305             }
12306             
12307             if(this.labelmd > 0){
12308                 labelCfg.cls += ' col-md-' + this.labelmd;
12309                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12310             }
12311             
12312             if(this.labelsm > 0){
12313                 labelCfg.cls += ' col-sm-' + this.labelsm;
12314                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12315             }
12316             
12317             if(this.labelxs > 0){
12318                 labelCfg.cls += ' col-xs-' + this.labelxs;
12319                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12320             }
12321             
12322             
12323         } else if ( this.fieldLabel.length) {
12324                 
12325             
12326             
12327             cfg.cn = [
12328                 {
12329                     tag : 'i',
12330                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12331                     tooltip : 'This field is required',
12332                     style : this.allowBlank ? ' display:none' : '' 
12333                 },
12334                 {
12335                     tag: 'label',
12336                    //cls : 'input-group-addon',
12337                     html : this.fieldLabel
12338
12339                 },
12340
12341                inputblock
12342
12343            ];
12344            
12345            if(this.indicatorpos == 'right'){
12346        
12347                 cfg.cn = [
12348                     {
12349                         tag: 'label',
12350                        //cls : 'input-group-addon',
12351                         html : this.fieldLabel
12352
12353                     },
12354                     {
12355                         tag : 'i',
12356                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12357                         tooltip : 'This field is required',
12358                         style : this.allowBlank ? ' display:none' : '' 
12359                     },
12360
12361                    inputblock
12362
12363                ];
12364
12365             }
12366
12367         } else {
12368             
12369             cfg.cn = [
12370
12371                     inputblock
12372
12373             ];
12374                 
12375                 
12376         };
12377         
12378         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12379            cfg.cls += ' navbar-form';
12380         }
12381         
12382         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12383             // on BS4 we do this only if not form 
12384             cfg.cls += ' navbar-form';
12385             cfg.tag = 'li';
12386         }
12387         
12388         return cfg;
12389         
12390     },
12391     /**
12392      * return the real input element.
12393      */
12394     inputEl: function ()
12395     {
12396         return this.el.select('input.form-control',true).first();
12397     },
12398     
12399     tooltipEl : function()
12400     {
12401         return this.inputEl();
12402     },
12403     
12404     indicatorEl : function()
12405     {
12406         if (Roo.bootstrap.version == 4) {
12407             return false; // not enabled in v4 yet.
12408         }
12409         
12410         var indicator = this.el.select('i.roo-required-indicator',true).first();
12411         
12412         if(!indicator){
12413             return false;
12414         }
12415         
12416         return indicator;
12417         
12418     },
12419     
12420     setDisabled : function(v)
12421     {
12422         var i  = this.inputEl().dom;
12423         if (!v) {
12424             i.removeAttribute('disabled');
12425             return;
12426             
12427         }
12428         i.setAttribute('disabled','true');
12429     },
12430     initEvents : function()
12431     {
12432           
12433         this.inputEl().on("keydown" , this.fireKey,  this);
12434         this.inputEl().on("focus", this.onFocus,  this);
12435         this.inputEl().on("blur", this.onBlur,  this);
12436         
12437         this.inputEl().relayEvent('keyup', this);
12438         this.inputEl().relayEvent('paste', this);
12439         
12440         this.indicator = this.indicatorEl();
12441         
12442         if(this.indicator){
12443             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12444         }
12445  
12446         // reference to original value for reset
12447         this.originalValue = this.getValue();
12448         //Roo.form.TextField.superclass.initEvents.call(this);
12449         if(this.validationEvent == 'keyup'){
12450             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12451             this.inputEl().on('keyup', this.filterValidation, this);
12452         }
12453         else if(this.validationEvent !== false){
12454             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12455         }
12456         
12457         if(this.selectOnFocus){
12458             this.on("focus", this.preFocus, this);
12459             
12460         }
12461         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12462             this.inputEl().on("keypress", this.filterKeys, this);
12463         } else {
12464             this.inputEl().relayEvent('keypress', this);
12465         }
12466        /* if(this.grow){
12467             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12468             this.el.on("click", this.autoSize,  this);
12469         }
12470         */
12471         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12472             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12473         }
12474         
12475         if (typeof(this.before) == 'object') {
12476             this.before.render(this.el.select('.roo-input-before',true).first());
12477         }
12478         if (typeof(this.after) == 'object') {
12479             this.after.render(this.el.select('.roo-input-after',true).first());
12480         }
12481         
12482         this.inputEl().on('change', this.onChange, this);
12483         
12484     },
12485     filterValidation : function(e){
12486         if(!e.isNavKeyPress()){
12487             this.validationTask.delay(this.validationDelay);
12488         }
12489     },
12490      /**
12491      * Validates the field value
12492      * @return {Boolean} True if the value is valid, else false
12493      */
12494     validate : function(){
12495         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12496         if(this.disabled || this.validateValue(this.getRawValue())){
12497             this.markValid();
12498             return true;
12499         }
12500         
12501         this.markInvalid();
12502         return false;
12503     },
12504     
12505     
12506     /**
12507      * Validates a value according to the field's validation rules and marks the field as invalid
12508      * if the validation fails
12509      * @param {Mixed} value The value to validate
12510      * @return {Boolean} True if the value is valid, else false
12511      */
12512     validateValue : function(value)
12513     {
12514         if(this.getVisibilityEl().hasClass('hidden')){
12515             return true;
12516         }
12517         
12518         if(value.length < 1)  { // if it's blank
12519             if(this.allowBlank){
12520                 return true;
12521             }
12522             return false;
12523         }
12524         
12525         if(value.length < this.minLength){
12526             return false;
12527         }
12528         if(value.length > this.maxLength){
12529             return false;
12530         }
12531         if(this.vtype){
12532             var vt = Roo.form.VTypes;
12533             if(!vt[this.vtype](value, this)){
12534                 return false;
12535             }
12536         }
12537         if(typeof this.validator == "function"){
12538             var msg = this.validator(value);
12539             if(msg !== true){
12540                 return false;
12541             }
12542             if (typeof(msg) == 'string') {
12543                 this.invalidText = msg;
12544             }
12545         }
12546         
12547         if(this.regex && !this.regex.test(value)){
12548             return false;
12549         }
12550         
12551         return true;
12552     },
12553     
12554      // private
12555     fireKey : function(e){
12556         //Roo.log('field ' + e.getKey());
12557         if(e.isNavKeyPress()){
12558             this.fireEvent("specialkey", this, e);
12559         }
12560     },
12561     focus : function (selectText){
12562         if(this.rendered){
12563             this.inputEl().focus();
12564             if(selectText === true){
12565                 this.inputEl().dom.select();
12566             }
12567         }
12568         return this;
12569     } ,
12570     
12571     onFocus : function(){
12572         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12573            // this.el.addClass(this.focusClass);
12574         }
12575         if(!this.hasFocus){
12576             this.hasFocus = true;
12577             this.startValue = this.getValue();
12578             this.fireEvent("focus", this);
12579         }
12580     },
12581     
12582     beforeBlur : Roo.emptyFn,
12583
12584     
12585     // private
12586     onBlur : function(){
12587         this.beforeBlur();
12588         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12589             //this.el.removeClass(this.focusClass);
12590         }
12591         this.hasFocus = false;
12592         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12593             this.validate();
12594         }
12595         var v = this.getValue();
12596         if(String(v) !== String(this.startValue)){
12597             this.fireEvent('change', this, v, this.startValue);
12598         }
12599         this.fireEvent("blur", this);
12600     },
12601     
12602     onChange : function(e)
12603     {
12604         var v = this.getValue();
12605         if(String(v) !== String(this.startValue)){
12606             this.fireEvent('change', this, v, this.startValue);
12607         }
12608         
12609     },
12610     
12611     /**
12612      * Resets the current field value to the originally loaded value and clears any validation messages
12613      */
12614     reset : function(){
12615         this.setValue(this.originalValue);
12616         this.validate();
12617     },
12618      /**
12619      * Returns the name of the field
12620      * @return {Mixed} name The name field
12621      */
12622     getName: function(){
12623         return this.name;
12624     },
12625      /**
12626      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12627      * @return {Mixed} value The field value
12628      */
12629     getValue : function(){
12630         
12631         var v = this.inputEl().getValue();
12632         
12633         return v;
12634     },
12635     /**
12636      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12637      * @return {Mixed} value The field value
12638      */
12639     getRawValue : function(){
12640         var v = this.inputEl().getValue();
12641         
12642         return v;
12643     },
12644     
12645     /**
12646      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12647      * @param {Mixed} value The value to set
12648      */
12649     setRawValue : function(v){
12650         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12651     },
12652     
12653     selectText : function(start, end){
12654         var v = this.getRawValue();
12655         if(v.length > 0){
12656             start = start === undefined ? 0 : start;
12657             end = end === undefined ? v.length : end;
12658             var d = this.inputEl().dom;
12659             if(d.setSelectionRange){
12660                 d.setSelectionRange(start, end);
12661             }else if(d.createTextRange){
12662                 var range = d.createTextRange();
12663                 range.moveStart("character", start);
12664                 range.moveEnd("character", v.length-end);
12665                 range.select();
12666             }
12667         }
12668     },
12669     
12670     /**
12671      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12672      * @param {Mixed} value The value to set
12673      */
12674     setValue : function(v){
12675         this.value = v;
12676         if(this.rendered){
12677             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12678             this.validate();
12679         }
12680     },
12681     
12682     /*
12683     processValue : function(value){
12684         if(this.stripCharsRe){
12685             var newValue = value.replace(this.stripCharsRe, '');
12686             if(newValue !== value){
12687                 this.setRawValue(newValue);
12688                 return newValue;
12689             }
12690         }
12691         return value;
12692     },
12693   */
12694     preFocus : function(){
12695         
12696         if(this.selectOnFocus){
12697             this.inputEl().dom.select();
12698         }
12699     },
12700     filterKeys : function(e){
12701         var k = e.getKey();
12702         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12703             return;
12704         }
12705         var c = e.getCharCode(), cc = String.fromCharCode(c);
12706         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12707             return;
12708         }
12709         if(!this.maskRe.test(cc)){
12710             e.stopEvent();
12711         }
12712     },
12713      /**
12714      * Clear any invalid styles/messages for this field
12715      */
12716     clearInvalid : function(){
12717         
12718         if(!this.el || this.preventMark){ // not rendered
12719             return;
12720         }
12721         
12722         
12723         this.el.removeClass([this.invalidClass, 'is-invalid']);
12724         
12725         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12726             
12727             var feedback = this.el.select('.form-control-feedback', true).first();
12728             
12729             if(feedback){
12730                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12731             }
12732             
12733         }
12734         
12735         if(this.indicator){
12736             this.indicator.removeClass('visible');
12737             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12738         }
12739         
12740         this.fireEvent('valid', this);
12741     },
12742     
12743      /**
12744      * Mark this field as valid
12745      */
12746     markValid : function()
12747     {
12748         if(!this.el  || this.preventMark){ // not rendered...
12749             return;
12750         }
12751         
12752         this.el.removeClass([this.invalidClass, this.validClass]);
12753         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12754
12755         var feedback = this.el.select('.form-control-feedback', true).first();
12756             
12757         if(feedback){
12758             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12759         }
12760         
12761         if(this.indicator){
12762             this.indicator.removeClass('visible');
12763             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12764         }
12765         
12766         if(this.disabled){
12767             return;
12768         }
12769         
12770            
12771         if(this.allowBlank && !this.getRawValue().length){
12772             return;
12773         }
12774         if (Roo.bootstrap.version == 3) {
12775             this.el.addClass(this.validClass);
12776         } else {
12777             this.inputEl().addClass('is-valid');
12778         }
12779
12780         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12781             
12782             var feedback = this.el.select('.form-control-feedback', true).first();
12783             
12784             if(feedback){
12785                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12786                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12787             }
12788             
12789         }
12790         
12791         this.fireEvent('valid', this);
12792     },
12793     
12794      /**
12795      * Mark this field as invalid
12796      * @param {String} msg The validation message
12797      */
12798     markInvalid : function(msg)
12799     {
12800         if(!this.el  || this.preventMark){ // not rendered
12801             return;
12802         }
12803         
12804         this.el.removeClass([this.invalidClass, this.validClass]);
12805         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12806         
12807         var feedback = this.el.select('.form-control-feedback', true).first();
12808             
12809         if(feedback){
12810             this.el.select('.form-control-feedback', true).first().removeClass(
12811                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12812         }
12813
12814         if(this.disabled){
12815             return;
12816         }
12817         
12818         if(this.allowBlank && !this.getRawValue().length){
12819             return;
12820         }
12821         
12822         if(this.indicator){
12823             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12824             this.indicator.addClass('visible');
12825         }
12826         if (Roo.bootstrap.version == 3) {
12827             this.el.addClass(this.invalidClass);
12828         } else {
12829             this.inputEl().addClass('is-invalid');
12830         }
12831         
12832         
12833         
12834         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12835             
12836             var feedback = this.el.select('.form-control-feedback', true).first();
12837             
12838             if(feedback){
12839                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12840                 
12841                 if(this.getValue().length || this.forceFeedback){
12842                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12843                 }
12844                 
12845             }
12846             
12847         }
12848         
12849         this.fireEvent('invalid', this, msg);
12850     },
12851     // private
12852     SafariOnKeyDown : function(event)
12853     {
12854         // this is a workaround for a password hang bug on chrome/ webkit.
12855         if (this.inputEl().dom.type != 'password') {
12856             return;
12857         }
12858         
12859         var isSelectAll = false;
12860         
12861         if(this.inputEl().dom.selectionEnd > 0){
12862             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12863         }
12864         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12865             event.preventDefault();
12866             this.setValue('');
12867             return;
12868         }
12869         
12870         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12871             
12872             event.preventDefault();
12873             // this is very hacky as keydown always get's upper case.
12874             //
12875             var cc = String.fromCharCode(event.getCharCode());
12876             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12877             
12878         }
12879     },
12880     adjustWidth : function(tag, w){
12881         tag = tag.toLowerCase();
12882         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12883             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12884                 if(tag == 'input'){
12885                     return w + 2;
12886                 }
12887                 if(tag == 'textarea'){
12888                     return w-2;
12889                 }
12890             }else if(Roo.isOpera){
12891                 if(tag == 'input'){
12892                     return w + 2;
12893                 }
12894                 if(tag == 'textarea'){
12895                     return w-2;
12896                 }
12897             }
12898         }
12899         return w;
12900     },
12901     
12902     setFieldLabel : function(v)
12903     {
12904         if(!this.rendered){
12905             return;
12906         }
12907         
12908         if(this.indicatorEl()){
12909             var ar = this.el.select('label > span',true);
12910             
12911             if (ar.elements.length) {
12912                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12913                 this.fieldLabel = v;
12914                 return;
12915             }
12916             
12917             var br = this.el.select('label',true);
12918             
12919             if(br.elements.length) {
12920                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12921                 this.fieldLabel = v;
12922                 return;
12923             }
12924             
12925             Roo.log('Cannot Found any of label > span || label in input');
12926             return;
12927         }
12928         
12929         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12930         this.fieldLabel = v;
12931         
12932         
12933     }
12934 });
12935
12936  
12937 /*
12938  * - LGPL
12939  *
12940  * Input
12941  * 
12942  */
12943
12944 /**
12945  * @class Roo.bootstrap.TextArea
12946  * @extends Roo.bootstrap.Input
12947  * Bootstrap TextArea class
12948  * @cfg {Number} cols Specifies the visible width of a text area
12949  * @cfg {Number} rows Specifies the visible number of lines in a text area
12950  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12951  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12952  * @cfg {string} html text
12953  * 
12954  * @constructor
12955  * Create a new TextArea
12956  * @param {Object} config The config object
12957  */
12958
12959 Roo.bootstrap.TextArea = function(config){
12960     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12961    
12962 };
12963
12964 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12965      
12966     cols : false,
12967     rows : 5,
12968     readOnly : false,
12969     warp : 'soft',
12970     resize : false,
12971     value: false,
12972     html: false,
12973     
12974     getAutoCreate : function(){
12975         
12976         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12977         
12978         var id = Roo.id();
12979         
12980         var cfg = {};
12981         
12982         if(this.inputType != 'hidden'){
12983             cfg.cls = 'form-group' //input-group
12984         }
12985         
12986         var input =  {
12987             tag: 'textarea',
12988             id : id,
12989             warp : this.warp,
12990             rows : this.rows,
12991             value : this.value || '',
12992             html: this.html || '',
12993             cls : 'form-control',
12994             placeholder : this.placeholder || '' 
12995             
12996         };
12997         
12998         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12999             input.maxLength = this.maxLength;
13000         }
13001         
13002         if(this.resize){
13003             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13004         }
13005         
13006         if(this.cols){
13007             input.cols = this.cols;
13008         }
13009         
13010         if (this.readOnly) {
13011             input.readonly = true;
13012         }
13013         
13014         if (this.name) {
13015             input.name = this.name;
13016         }
13017         
13018         if (this.size) {
13019             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13020         }
13021         
13022         var settings=this;
13023         ['xs','sm','md','lg'].map(function(size){
13024             if (settings[size]) {
13025                 cfg.cls += ' col-' + size + '-' + settings[size];
13026             }
13027         });
13028         
13029         var inputblock = input;
13030         
13031         if(this.hasFeedback && !this.allowBlank){
13032             
13033             var feedback = {
13034                 tag: 'span',
13035                 cls: 'glyphicon form-control-feedback'
13036             };
13037
13038             inputblock = {
13039                 cls : 'has-feedback',
13040                 cn :  [
13041                     input,
13042                     feedback
13043                 ] 
13044             };  
13045         }
13046         
13047         
13048         if (this.before || this.after) {
13049             
13050             inputblock = {
13051                 cls : 'input-group',
13052                 cn :  [] 
13053             };
13054             if (this.before) {
13055                 inputblock.cn.push({
13056                     tag :'span',
13057                     cls : 'input-group-addon',
13058                     html : this.before
13059                 });
13060             }
13061             
13062             inputblock.cn.push(input);
13063             
13064             if(this.hasFeedback && !this.allowBlank){
13065                 inputblock.cls += ' has-feedback';
13066                 inputblock.cn.push(feedback);
13067             }
13068             
13069             if (this.after) {
13070                 inputblock.cn.push({
13071                     tag :'span',
13072                     cls : 'input-group-addon',
13073                     html : this.after
13074                 });
13075             }
13076             
13077         }
13078         
13079         if (align ==='left' && this.fieldLabel.length) {
13080             cfg.cn = [
13081                 {
13082                     tag: 'label',
13083                     'for' :  id,
13084                     cls : 'control-label',
13085                     html : this.fieldLabel
13086                 },
13087                 {
13088                     cls : "",
13089                     cn: [
13090                         inputblock
13091                     ]
13092                 }
13093
13094             ];
13095             
13096             if(this.labelWidth > 12){
13097                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13098             }
13099
13100             if(this.labelWidth < 13 && this.labelmd == 0){
13101                 this.labelmd = this.labelWidth;
13102             }
13103
13104             if(this.labellg > 0){
13105                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13106                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13107             }
13108
13109             if(this.labelmd > 0){
13110                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13111                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13112             }
13113
13114             if(this.labelsm > 0){
13115                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13116                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13117             }
13118
13119             if(this.labelxs > 0){
13120                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13121                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13122             }
13123             
13124         } else if ( this.fieldLabel.length) {
13125             cfg.cn = [
13126
13127                {
13128                    tag: 'label',
13129                    //cls : 'input-group-addon',
13130                    html : this.fieldLabel
13131
13132                },
13133
13134                inputblock
13135
13136            ];
13137
13138         } else {
13139
13140             cfg.cn = [
13141
13142                 inputblock
13143
13144             ];
13145                 
13146         }
13147         
13148         if (this.disabled) {
13149             input.disabled=true;
13150         }
13151         
13152         return cfg;
13153         
13154     },
13155     /**
13156      * return the real textarea element.
13157      */
13158     inputEl: function ()
13159     {
13160         return this.el.select('textarea.form-control',true).first();
13161     },
13162     
13163     /**
13164      * Clear any invalid styles/messages for this field
13165      */
13166     clearInvalid : function()
13167     {
13168         
13169         if(!this.el || this.preventMark){ // not rendered
13170             return;
13171         }
13172         
13173         var label = this.el.select('label', true).first();
13174         var icon = this.el.select('i.fa-star', true).first();
13175         
13176         if(label && icon){
13177             icon.remove();
13178         }
13179         this.el.removeClass( this.validClass);
13180         this.inputEl().removeClass('is-invalid');
13181          
13182         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13183             
13184             var feedback = this.el.select('.form-control-feedback', true).first();
13185             
13186             if(feedback){
13187                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13188             }
13189             
13190         }
13191         
13192         this.fireEvent('valid', this);
13193     },
13194     
13195      /**
13196      * Mark this field as valid
13197      */
13198     markValid : function()
13199     {
13200         if(!this.el  || this.preventMark){ // not rendered
13201             return;
13202         }
13203         
13204         this.el.removeClass([this.invalidClass, this.validClass]);
13205         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13206         
13207         var feedback = this.el.select('.form-control-feedback', true).first();
13208             
13209         if(feedback){
13210             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13211         }
13212
13213         if(this.disabled || this.allowBlank){
13214             return;
13215         }
13216         
13217         var label = this.el.select('label', true).first();
13218         var icon = this.el.select('i.fa-star', true).first();
13219         
13220         if(label && icon){
13221             icon.remove();
13222         }
13223         if (Roo.bootstrap.version == 3) {
13224             this.el.addClass(this.validClass);
13225         } else {
13226             this.inputEl().addClass('is-valid');
13227         }
13228         
13229         
13230         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13231             
13232             var feedback = this.el.select('.form-control-feedback', true).first();
13233             
13234             if(feedback){
13235                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13236                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13237             }
13238             
13239         }
13240         
13241         this.fireEvent('valid', this);
13242     },
13243     
13244      /**
13245      * Mark this field as invalid
13246      * @param {String} msg The validation message
13247      */
13248     markInvalid : function(msg)
13249     {
13250         if(!this.el  || this.preventMark){ // not rendered
13251             return;
13252         }
13253         
13254         this.el.removeClass([this.invalidClass, this.validClass]);
13255         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13256         
13257         var feedback = this.el.select('.form-control-feedback', true).first();
13258             
13259         if(feedback){
13260             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13261         }
13262
13263         if(this.disabled || this.allowBlank){
13264             return;
13265         }
13266         
13267         var label = this.el.select('label', true).first();
13268         var icon = this.el.select('i.fa-star', true).first();
13269         
13270         if(!this.getValue().length && label && !icon){
13271             this.el.createChild({
13272                 tag : 'i',
13273                 cls : 'text-danger fa fa-lg fa-star',
13274                 tooltip : 'This field is required',
13275                 style : 'margin-right:5px;'
13276             }, label, true);
13277         }
13278         
13279         if (Roo.bootstrap.version == 3) {
13280             this.el.addClass(this.invalidClass);
13281         } else {
13282             this.inputEl().addClass('is-invalid');
13283         }
13284         
13285         // fixme ... this may be depricated need to test..
13286         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13287             
13288             var feedback = this.el.select('.form-control-feedback', true).first();
13289             
13290             if(feedback){
13291                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13292                 
13293                 if(this.getValue().length || this.forceFeedback){
13294                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13295                 }
13296                 
13297             }
13298             
13299         }
13300         
13301         this.fireEvent('invalid', this, msg);
13302     }
13303 });
13304
13305  
13306 /*
13307  * - LGPL
13308  *
13309  * trigger field - base class for combo..
13310  * 
13311  */
13312  
13313 /**
13314  * @class Roo.bootstrap.TriggerField
13315  * @extends Roo.bootstrap.Input
13316  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13317  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13318  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13319  * for which you can provide a custom implementation.  For example:
13320  * <pre><code>
13321 var trigger = new Roo.bootstrap.TriggerField();
13322 trigger.onTriggerClick = myTriggerFn;
13323 trigger.applyTo('my-field');
13324 </code></pre>
13325  *
13326  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13327  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13328  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13329  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13330  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13331
13332  * @constructor
13333  * Create a new TriggerField.
13334  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13335  * to the base TextField)
13336  */
13337 Roo.bootstrap.TriggerField = function(config){
13338     this.mimicing = false;
13339     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13340 };
13341
13342 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13343     /**
13344      * @cfg {String} triggerClass A CSS class to apply to the trigger
13345      */
13346      /**
13347      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13348      */
13349     hideTrigger:false,
13350
13351     /**
13352      * @cfg {Boolean} removable (true|false) special filter default false
13353      */
13354     removable : false,
13355     
13356     /** @cfg {Boolean} grow @hide */
13357     /** @cfg {Number} growMin @hide */
13358     /** @cfg {Number} growMax @hide */
13359
13360     /**
13361      * @hide 
13362      * @method
13363      */
13364     autoSize: Roo.emptyFn,
13365     // private
13366     monitorTab : true,
13367     // private
13368     deferHeight : true,
13369
13370     
13371     actionMode : 'wrap',
13372     
13373     caret : false,
13374     
13375     
13376     getAutoCreate : function(){
13377        
13378         var align = this.labelAlign || this.parentLabelAlign();
13379         
13380         var id = Roo.id();
13381         
13382         var cfg = {
13383             cls: 'form-group' //input-group
13384         };
13385         
13386         
13387         var input =  {
13388             tag: 'input',
13389             id : id,
13390             type : this.inputType,
13391             cls : 'form-control',
13392             autocomplete: 'new-password',
13393             placeholder : this.placeholder || '' 
13394             
13395         };
13396         if (this.name) {
13397             input.name = this.name;
13398         }
13399         if (this.size) {
13400             input.cls += ' input-' + this.size;
13401         }
13402         
13403         if (this.disabled) {
13404             input.disabled=true;
13405         }
13406         
13407         var inputblock = input;
13408         
13409         if(this.hasFeedback && !this.allowBlank){
13410             
13411             var feedback = {
13412                 tag: 'span',
13413                 cls: 'glyphicon form-control-feedback'
13414             };
13415             
13416             if(this.removable && !this.editable  ){
13417                 inputblock = {
13418                     cls : 'has-feedback',
13419                     cn :  [
13420                         inputblock,
13421                         {
13422                             tag: 'button',
13423                             html : 'x',
13424                             cls : 'roo-combo-removable-btn close'
13425                         },
13426                         feedback
13427                     ] 
13428                 };
13429             } else {
13430                 inputblock = {
13431                     cls : 'has-feedback',
13432                     cn :  [
13433                         inputblock,
13434                         feedback
13435                     ] 
13436                 };
13437             }
13438
13439         } else {
13440             if(this.removable && !this.editable ){
13441                 inputblock = {
13442                     cls : 'roo-removable',
13443                     cn :  [
13444                         inputblock,
13445                         {
13446                             tag: 'button',
13447                             html : 'x',
13448                             cls : 'roo-combo-removable-btn close'
13449                         }
13450                     ] 
13451                 };
13452             }
13453         }
13454         
13455         if (this.before || this.after) {
13456             
13457             inputblock = {
13458                 cls : 'input-group',
13459                 cn :  [] 
13460             };
13461             if (this.before) {
13462                 inputblock.cn.push({
13463                     tag :'span',
13464                     cls : 'input-group-addon input-group-prepend input-group-text',
13465                     html : this.before
13466                 });
13467             }
13468             
13469             inputblock.cn.push(input);
13470             
13471             if(this.hasFeedback && !this.allowBlank){
13472                 inputblock.cls += ' has-feedback';
13473                 inputblock.cn.push(feedback);
13474             }
13475             
13476             if (this.after) {
13477                 inputblock.cn.push({
13478                     tag :'span',
13479                     cls : 'input-group-addon input-group-append input-group-text',
13480                     html : this.after
13481                 });
13482             }
13483             
13484         };
13485         
13486       
13487         
13488         var ibwrap = inputblock;
13489         
13490         if(this.multiple){
13491             ibwrap = {
13492                 tag: 'ul',
13493                 cls: 'roo-select2-choices',
13494                 cn:[
13495                     {
13496                         tag: 'li',
13497                         cls: 'roo-select2-search-field',
13498                         cn: [
13499
13500                             inputblock
13501                         ]
13502                     }
13503                 ]
13504             };
13505                 
13506         }
13507         
13508         var combobox = {
13509             cls: 'roo-select2-container input-group',
13510             cn: [
13511                  {
13512                     tag: 'input',
13513                     type : 'hidden',
13514                     cls: 'form-hidden-field'
13515                 },
13516                 ibwrap
13517             ]
13518         };
13519         
13520         if(!this.multiple && this.showToggleBtn){
13521             
13522             var caret = {
13523                         tag: 'span',
13524                         cls: 'caret'
13525              };
13526             if (this.caret != false) {
13527                 caret = {
13528                      tag: 'i',
13529                      cls: 'fa fa-' + this.caret
13530                 };
13531                 
13532             }
13533             
13534             combobox.cn.push({
13535                 tag :'span',
13536                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13537                 cn : [
13538                     Roo.bootstrap.version == 3 ? caret : '',
13539                     {
13540                         tag: 'span',
13541                         cls: 'combobox-clear',
13542                         cn  : [
13543                             {
13544                                 tag : 'i',
13545                                 cls: 'icon-remove'
13546                             }
13547                         ]
13548                     }
13549                 ]
13550
13551             })
13552         }
13553         
13554         if(this.multiple){
13555             combobox.cls += ' roo-select2-container-multi';
13556         }
13557          var indicator = {
13558             tag : 'i',
13559             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13560             tooltip : 'This field is required'
13561         };
13562         if (Roo.bootstrap.version == 4) {
13563             indicator = {
13564                 tag : 'i',
13565                 style : 'display:none'
13566             };
13567         }
13568         
13569         
13570         if (align ==='left' && this.fieldLabel.length) {
13571             
13572             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13573
13574             cfg.cn = [
13575                 indicator,
13576                 {
13577                     tag: 'label',
13578                     'for' :  id,
13579                     cls : 'control-label',
13580                     html : this.fieldLabel
13581
13582                 },
13583                 {
13584                     cls : "", 
13585                     cn: [
13586                         combobox
13587                     ]
13588                 }
13589
13590             ];
13591             
13592             var labelCfg = cfg.cn[1];
13593             var contentCfg = cfg.cn[2];
13594             
13595             if(this.indicatorpos == 'right'){
13596                 cfg.cn = [
13597                     {
13598                         tag: 'label',
13599                         'for' :  id,
13600                         cls : 'control-label',
13601                         cn : [
13602                             {
13603                                 tag : 'span',
13604                                 html : this.fieldLabel
13605                             },
13606                             indicator
13607                         ]
13608                     },
13609                     {
13610                         cls : "", 
13611                         cn: [
13612                             combobox
13613                         ]
13614                     }
13615
13616                 ];
13617                 
13618                 labelCfg = cfg.cn[0];
13619                 contentCfg = cfg.cn[1];
13620             }
13621             
13622             if(this.labelWidth > 12){
13623                 labelCfg.style = "width: " + this.labelWidth + 'px';
13624             }
13625             
13626             if(this.labelWidth < 13 && this.labelmd == 0){
13627                 this.labelmd = this.labelWidth;
13628             }
13629             
13630             if(this.labellg > 0){
13631                 labelCfg.cls += ' col-lg-' + this.labellg;
13632                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13633             }
13634             
13635             if(this.labelmd > 0){
13636                 labelCfg.cls += ' col-md-' + this.labelmd;
13637                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13638             }
13639             
13640             if(this.labelsm > 0){
13641                 labelCfg.cls += ' col-sm-' + this.labelsm;
13642                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13643             }
13644             
13645             if(this.labelxs > 0){
13646                 labelCfg.cls += ' col-xs-' + this.labelxs;
13647                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13648             }
13649             
13650         } else if ( this.fieldLabel.length) {
13651 //                Roo.log(" label");
13652             cfg.cn = [
13653                 indicator,
13654                {
13655                    tag: 'label',
13656                    //cls : 'input-group-addon',
13657                    html : this.fieldLabel
13658
13659                },
13660
13661                combobox
13662
13663             ];
13664             
13665             if(this.indicatorpos == 'right'){
13666                 
13667                 cfg.cn = [
13668                     {
13669                        tag: 'label',
13670                        cn : [
13671                            {
13672                                tag : 'span',
13673                                html : this.fieldLabel
13674                            },
13675                            indicator
13676                        ]
13677
13678                     },
13679                     combobox
13680
13681                 ];
13682
13683             }
13684
13685         } else {
13686             
13687 //                Roo.log(" no label && no align");
13688                 cfg = combobox
13689                      
13690                 
13691         }
13692         
13693         var settings=this;
13694         ['xs','sm','md','lg'].map(function(size){
13695             if (settings[size]) {
13696                 cfg.cls += ' col-' + size + '-' + settings[size];
13697             }
13698         });
13699         
13700         return cfg;
13701         
13702     },
13703     
13704     
13705     
13706     // private
13707     onResize : function(w, h){
13708 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13709 //        if(typeof w == 'number'){
13710 //            var x = w - this.trigger.getWidth();
13711 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13712 //            this.trigger.setStyle('left', x+'px');
13713 //        }
13714     },
13715
13716     // private
13717     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13718
13719     // private
13720     getResizeEl : function(){
13721         return this.inputEl();
13722     },
13723
13724     // private
13725     getPositionEl : function(){
13726         return this.inputEl();
13727     },
13728
13729     // private
13730     alignErrorIcon : function(){
13731         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13732     },
13733
13734     // private
13735     initEvents : function(){
13736         
13737         this.createList();
13738         
13739         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13740         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13741         if(!this.multiple && this.showToggleBtn){
13742             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13743             if(this.hideTrigger){
13744                 this.trigger.setDisplayed(false);
13745             }
13746             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13747         }
13748         
13749         if(this.multiple){
13750             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13751         }
13752         
13753         if(this.removable && !this.editable && !this.tickable){
13754             var close = this.closeTriggerEl();
13755             
13756             if(close){
13757                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13758                 close.on('click', this.removeBtnClick, this, close);
13759             }
13760         }
13761         
13762         //this.trigger.addClassOnOver('x-form-trigger-over');
13763         //this.trigger.addClassOnClick('x-form-trigger-click');
13764         
13765         //if(!this.width){
13766         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13767         //}
13768     },
13769     
13770     closeTriggerEl : function()
13771     {
13772         var close = this.el.select('.roo-combo-removable-btn', true).first();
13773         return close ? close : false;
13774     },
13775     
13776     removeBtnClick : function(e, h, el)
13777     {
13778         e.preventDefault();
13779         
13780         if(this.fireEvent("remove", this) !== false){
13781             this.reset();
13782             this.fireEvent("afterremove", this)
13783         }
13784     },
13785     
13786     createList : function()
13787     {
13788         this.list = Roo.get(document.body).createChild({
13789             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13790             cls: 'typeahead typeahead-long dropdown-menu shadow',
13791             style: 'display:none'
13792         });
13793         
13794         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13795         
13796     },
13797
13798     // private
13799     initTrigger : function(){
13800        
13801     },
13802
13803     // private
13804     onDestroy : function(){
13805         if(this.trigger){
13806             this.trigger.removeAllListeners();
13807           //  this.trigger.remove();
13808         }
13809         //if(this.wrap){
13810         //    this.wrap.remove();
13811         //}
13812         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13813     },
13814
13815     // private
13816     onFocus : function(){
13817         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13818         /*
13819         if(!this.mimicing){
13820             this.wrap.addClass('x-trigger-wrap-focus');
13821             this.mimicing = true;
13822             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13823             if(this.monitorTab){
13824                 this.el.on("keydown", this.checkTab, this);
13825             }
13826         }
13827         */
13828     },
13829
13830     // private
13831     checkTab : function(e){
13832         if(e.getKey() == e.TAB){
13833             this.triggerBlur();
13834         }
13835     },
13836
13837     // private
13838     onBlur : function(){
13839         // do nothing
13840     },
13841
13842     // private
13843     mimicBlur : function(e, t){
13844         /*
13845         if(!this.wrap.contains(t) && this.validateBlur()){
13846             this.triggerBlur();
13847         }
13848         */
13849     },
13850
13851     // private
13852     triggerBlur : function(){
13853         this.mimicing = false;
13854         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13855         if(this.monitorTab){
13856             this.el.un("keydown", this.checkTab, this);
13857         }
13858         //this.wrap.removeClass('x-trigger-wrap-focus');
13859         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13860     },
13861
13862     // private
13863     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13864     validateBlur : function(e, t){
13865         return true;
13866     },
13867
13868     // private
13869     onDisable : function(){
13870         this.inputEl().dom.disabled = true;
13871         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13872         //if(this.wrap){
13873         //    this.wrap.addClass('x-item-disabled');
13874         //}
13875     },
13876
13877     // private
13878     onEnable : function(){
13879         this.inputEl().dom.disabled = false;
13880         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13881         //if(this.wrap){
13882         //    this.el.removeClass('x-item-disabled');
13883         //}
13884     },
13885
13886     // private
13887     onShow : function(){
13888         var ae = this.getActionEl();
13889         
13890         if(ae){
13891             ae.dom.style.display = '';
13892             ae.dom.style.visibility = 'visible';
13893         }
13894     },
13895
13896     // private
13897     
13898     onHide : function(){
13899         var ae = this.getActionEl();
13900         ae.dom.style.display = 'none';
13901     },
13902
13903     /**
13904      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13905      * by an implementing function.
13906      * @method
13907      * @param {EventObject} e
13908      */
13909     onTriggerClick : Roo.emptyFn
13910 });
13911  
13912 /*
13913 * Licence: LGPL
13914 */
13915
13916 /**
13917  * @class Roo.bootstrap.CardUploader
13918  * @extends Roo.bootstrap.Button
13919  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13920  * @cfg {Number} errorTimeout default 3000
13921  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13922  * @cfg {Array}  html The button text.
13923
13924  *
13925  * @constructor
13926  * Create a new CardUploader
13927  * @param {Object} config The config object
13928  */
13929
13930 Roo.bootstrap.CardUploader = function(config){
13931     
13932  
13933     
13934     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13935     
13936     
13937     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13938         return r.data.id
13939      });
13940     
13941      this.addEvents({
13942          // raw events
13943         /**
13944          * @event preview
13945          * When a image is clicked on - and needs to display a slideshow or similar..
13946          * @param {Roo.bootstrap.Card} this
13947          * @param {Object} The image information data 
13948          *
13949          */
13950         'preview' : true,
13951          /**
13952          * @event download
13953          * When a the download link is clicked
13954          * @param {Roo.bootstrap.Card} this
13955          * @param {Object} The image information data  contains 
13956          */
13957         'download' : true
13958         
13959     });
13960 };
13961  
13962 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13963     
13964      
13965     errorTimeout : 3000,
13966      
13967     images : false,
13968    
13969     fileCollection : false,
13970     allowBlank : true,
13971     
13972     getAutoCreate : function()
13973     {
13974         
13975         var cfg =  {
13976             cls :'form-group' ,
13977             cn : [
13978                
13979                 {
13980                     tag: 'label',
13981                    //cls : 'input-group-addon',
13982                     html : this.fieldLabel
13983
13984                 },
13985
13986                 {
13987                     tag: 'input',
13988                     type : 'hidden',
13989                     name : this.name,
13990                     value : this.value,
13991                     cls : 'd-none  form-control'
13992                 },
13993                 
13994                 {
13995                     tag: 'input',
13996                     multiple : 'multiple',
13997                     type : 'file',
13998                     cls : 'd-none  roo-card-upload-selector'
13999                 },
14000                 
14001                 {
14002                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14003                 },
14004                 {
14005                     cls : 'card-columns roo-card-uploader-container'
14006                 }
14007
14008             ]
14009         };
14010            
14011          
14012         return cfg;
14013     },
14014     
14015     getChildContainer : function() /// what children are added to.
14016     {
14017         return this.containerEl;
14018     },
14019    
14020     getButtonContainer : function() /// what children are added to.
14021     {
14022         return this.el.select(".roo-card-uploader-button-container").first();
14023     },
14024    
14025     initEvents : function()
14026     {
14027         
14028         Roo.bootstrap.Input.prototype.initEvents.call(this);
14029         
14030         var t = this;
14031         this.addxtype({
14032             xns: Roo.bootstrap,
14033
14034             xtype : 'Button',
14035             container_method : 'getButtonContainer' ,            
14036             html :  this.html, // fix changable?
14037             cls : 'w-100 ',
14038             listeners : {
14039                 'click' : function(btn, e) {
14040                     t.onClick(e);
14041                 }
14042             }
14043         });
14044         
14045         
14046         
14047         
14048         this.urlAPI = (window.createObjectURL && window) || 
14049                                 (window.URL && URL.revokeObjectURL && URL) || 
14050                                 (window.webkitURL && webkitURL);
14051                         
14052          
14053          
14054          
14055         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14056         
14057         this.selectorEl.on('change', this.onFileSelected, this);
14058         if (this.images) {
14059             var t = this;
14060             this.images.forEach(function(img) {
14061                 t.addCard(img)
14062             });
14063             this.images = false;
14064         }
14065         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14066          
14067        
14068     },
14069     
14070    
14071     onClick : function(e)
14072     {
14073         e.preventDefault();
14074          
14075         this.selectorEl.dom.click();
14076          
14077     },
14078     
14079     onFileSelected : function(e)
14080     {
14081         e.preventDefault();
14082         
14083         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14084             return;
14085         }
14086         
14087         Roo.each(this.selectorEl.dom.files, function(file){    
14088             this.addFile(file);
14089         }, this);
14090          
14091     },
14092     
14093       
14094     
14095       
14096     
14097     addFile : function(file)
14098     {
14099            
14100         if(typeof(file) === 'string'){
14101             throw "Add file by name?"; // should not happen
14102             return;
14103         }
14104         
14105         if(!file || !this.urlAPI){
14106             return;
14107         }
14108         
14109         // file;
14110         // file.type;
14111         
14112         var _this = this;
14113         
14114         
14115         var url = _this.urlAPI.createObjectURL( file);
14116            
14117         this.addCard({
14118             id : Roo.bootstrap.CardUploader.ID--,
14119             is_uploaded : false,
14120             src : url,
14121             srcfile : file,
14122             title : file.name,
14123             mimetype : file.type,
14124             preview : false,
14125             is_deleted : 0
14126         });
14127         
14128     },
14129     
14130     /**
14131      * addCard - add an Attachment to the uploader
14132      * @param data - the data about the image to upload
14133      *
14134      * {
14135           id : 123
14136           title : "Title of file",
14137           is_uploaded : false,
14138           src : "http://.....",
14139           srcfile : { the File upload object },
14140           mimetype : file.type,
14141           preview : false,
14142           is_deleted : 0
14143           .. any other data...
14144         }
14145      *
14146      * 
14147     */
14148     
14149     addCard : function (data)
14150     {
14151         // hidden input element?
14152         // if the file is not an image...
14153         //then we need to use something other that and header_image
14154         var t = this;
14155         //   remove.....
14156         var footer = [
14157             {
14158                 xns : Roo.bootstrap,
14159                 xtype : 'CardFooter',
14160                  items: [
14161                     {
14162                         xns : Roo.bootstrap,
14163                         xtype : 'Element',
14164                         cls : 'd-flex',
14165                         items : [
14166                             
14167                             {
14168                                 xns : Roo.bootstrap,
14169                                 xtype : 'Button',
14170                                 html : String.format("<small>{0}</small>", data.title),
14171                                 cls : 'col-10 text-left',
14172                                 size: 'sm',
14173                                 weight: 'link',
14174                                 fa : 'download',
14175                                 listeners : {
14176                                     click : function() {
14177                                      
14178                                         t.fireEvent( "download", t, data );
14179                                     }
14180                                 }
14181                             },
14182                           
14183                             {
14184                                 xns : Roo.bootstrap,
14185                                 xtype : 'Button',
14186                                 style: 'max-height: 28px; ',
14187                                 size : 'sm',
14188                                 weight: 'danger',
14189                                 cls : 'col-2',
14190                                 fa : 'times',
14191                                 listeners : {
14192                                     click : function() {
14193                                         t.removeCard(data.id)
14194                                     }
14195                                 }
14196                             }
14197                         ]
14198                     }
14199                     
14200                 ] 
14201             }
14202             
14203         ];
14204         
14205         var cn = this.addxtype(
14206             {
14207                  
14208                 xns : Roo.bootstrap,
14209                 xtype : 'Card',
14210                 closeable : true,
14211                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14212                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14213                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14214                 data : data,
14215                 html : false,
14216                  
14217                 items : footer,
14218                 initEvents : function() {
14219                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14220                     var card = this;
14221                     this.imgEl = this.el.select('.card-img-top').first();
14222                     if (this.imgEl) {
14223                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14224                         this.imgEl.set({ 'pointer' : 'cursor' });
14225                                   
14226                     }
14227                     this.getCardFooter().addClass('p-1');
14228                     
14229                   
14230                 }
14231                 
14232             }
14233         );
14234         // dont' really need ot update items.
14235         // this.items.push(cn);
14236         this.fileCollection.add(cn);
14237         
14238         if (!data.srcfile) {
14239             this.updateInput();
14240             return;
14241         }
14242             
14243         var _t = this;
14244         var reader = new FileReader();
14245         reader.addEventListener("load", function() {  
14246             data.srcdata =  reader.result;
14247             _t.updateInput();
14248         });
14249         reader.readAsDataURL(data.srcfile);
14250         
14251         
14252         
14253     },
14254     removeCard : function(id)
14255     {
14256         
14257         var card  = this.fileCollection.get(id);
14258         card.data.is_deleted = 1;
14259         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14260         //this.fileCollection.remove(card);
14261         //this.items = this.items.filter(function(e) { return e != card });
14262         // dont' really need ot update items.
14263         card.el.dom.parentNode.removeChild(card.el.dom);
14264         this.updateInput();
14265
14266         
14267     },
14268     reset: function()
14269     {
14270         this.fileCollection.each(function(card) {
14271             if (card.el.dom && card.el.dom.parentNode) {
14272                 card.el.dom.parentNode.removeChild(card.el.dom);
14273             }
14274         });
14275         this.fileCollection.clear();
14276         this.updateInput();
14277     },
14278     
14279     updateInput : function()
14280     {
14281          var data = [];
14282         this.fileCollection.each(function(e) {
14283             data.push(e.data);
14284             
14285         });
14286         this.inputEl().dom.value = JSON.stringify(data);
14287         
14288         
14289         
14290     }
14291     
14292     
14293 });
14294
14295
14296 Roo.bootstrap.CardUploader.ID = -1;/*
14297  * Based on:
14298  * Ext JS Library 1.1.1
14299  * Copyright(c) 2006-2007, Ext JS, LLC.
14300  *
14301  * Originally Released Under LGPL - original licence link has changed is not relivant.
14302  *
14303  * Fork - LGPL
14304  * <script type="text/javascript">
14305  */
14306
14307
14308 /**
14309  * @class Roo.data.SortTypes
14310  * @singleton
14311  * Defines the default sorting (casting?) comparison functions used when sorting data.
14312  */
14313 Roo.data.SortTypes = {
14314     /**
14315      * Default sort that does nothing
14316      * @param {Mixed} s The value being converted
14317      * @return {Mixed} The comparison value
14318      */
14319     none : function(s){
14320         return s;
14321     },
14322     
14323     /**
14324      * The regular expression used to strip tags
14325      * @type {RegExp}
14326      * @property
14327      */
14328     stripTagsRE : /<\/?[^>]+>/gi,
14329     
14330     /**
14331      * Strips all HTML tags to sort on text only
14332      * @param {Mixed} s The value being converted
14333      * @return {String} The comparison value
14334      */
14335     asText : function(s){
14336         return String(s).replace(this.stripTagsRE, "");
14337     },
14338     
14339     /**
14340      * Strips all HTML tags to sort on text only - Case insensitive
14341      * @param {Mixed} s The value being converted
14342      * @return {String} The comparison value
14343      */
14344     asUCText : function(s){
14345         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14346     },
14347     
14348     /**
14349      * Case insensitive string
14350      * @param {Mixed} s The value being converted
14351      * @return {String} The comparison value
14352      */
14353     asUCString : function(s) {
14354         return String(s).toUpperCase();
14355     },
14356     
14357     /**
14358      * Date sorting
14359      * @param {Mixed} s The value being converted
14360      * @return {Number} The comparison value
14361      */
14362     asDate : function(s) {
14363         if(!s){
14364             return 0;
14365         }
14366         if(s instanceof Date){
14367             return s.getTime();
14368         }
14369         return Date.parse(String(s));
14370     },
14371     
14372     /**
14373      * Float sorting
14374      * @param {Mixed} s The value being converted
14375      * @return {Float} The comparison value
14376      */
14377     asFloat : function(s) {
14378         var val = parseFloat(String(s).replace(/,/g, ""));
14379         if(isNaN(val)) {
14380             val = 0;
14381         }
14382         return val;
14383     },
14384     
14385     /**
14386      * Integer sorting
14387      * @param {Mixed} s The value being converted
14388      * @return {Number} The comparison value
14389      */
14390     asInt : function(s) {
14391         var val = parseInt(String(s).replace(/,/g, ""));
14392         if(isNaN(val)) {
14393             val = 0;
14394         }
14395         return val;
14396     }
14397 };/*
14398  * Based on:
14399  * Ext JS Library 1.1.1
14400  * Copyright(c) 2006-2007, Ext JS, LLC.
14401  *
14402  * Originally Released Under LGPL - original licence link has changed is not relivant.
14403  *
14404  * Fork - LGPL
14405  * <script type="text/javascript">
14406  */
14407
14408 /**
14409 * @class Roo.data.Record
14410  * Instances of this class encapsulate both record <em>definition</em> information, and record
14411  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14412  * to access Records cached in an {@link Roo.data.Store} object.<br>
14413  * <p>
14414  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14415  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14416  * objects.<br>
14417  * <p>
14418  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14419  * @constructor
14420  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14421  * {@link #create}. The parameters are the same.
14422  * @param {Array} data An associative Array of data values keyed by the field name.
14423  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14424  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14425  * not specified an integer id is generated.
14426  */
14427 Roo.data.Record = function(data, id){
14428     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14429     this.data = data;
14430 };
14431
14432 /**
14433  * Generate a constructor for a specific record layout.
14434  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14435  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14436  * Each field definition object may contain the following properties: <ul>
14437  * <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,
14438  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14439  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14440  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14441  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14442  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14443  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14444  * this may be omitted.</p></li>
14445  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14446  * <ul><li>auto (Default, implies no conversion)</li>
14447  * <li>string</li>
14448  * <li>int</li>
14449  * <li>float</li>
14450  * <li>boolean</li>
14451  * <li>date</li></ul></p></li>
14452  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14453  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14454  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14455  * by the Reader into an object that will be stored in the Record. It is passed the
14456  * following parameters:<ul>
14457  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14458  * </ul></p></li>
14459  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14460  * </ul>
14461  * <br>usage:<br><pre><code>
14462 var TopicRecord = Roo.data.Record.create(
14463     {name: 'title', mapping: 'topic_title'},
14464     {name: 'author', mapping: 'username'},
14465     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14466     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14467     {name: 'lastPoster', mapping: 'user2'},
14468     {name: 'excerpt', mapping: 'post_text'}
14469 );
14470
14471 var myNewRecord = new TopicRecord({
14472     title: 'Do my job please',
14473     author: 'noobie',
14474     totalPosts: 1,
14475     lastPost: new Date(),
14476     lastPoster: 'Animal',
14477     excerpt: 'No way dude!'
14478 });
14479 myStore.add(myNewRecord);
14480 </code></pre>
14481  * @method create
14482  * @static
14483  */
14484 Roo.data.Record.create = function(o){
14485     var f = function(){
14486         f.superclass.constructor.apply(this, arguments);
14487     };
14488     Roo.extend(f, Roo.data.Record);
14489     var p = f.prototype;
14490     p.fields = new Roo.util.MixedCollection(false, function(field){
14491         return field.name;
14492     });
14493     for(var i = 0, len = o.length; i < len; i++){
14494         p.fields.add(new Roo.data.Field(o[i]));
14495     }
14496     f.getField = function(name){
14497         return p.fields.get(name);  
14498     };
14499     return f;
14500 };
14501
14502 Roo.data.Record.AUTO_ID = 1000;
14503 Roo.data.Record.EDIT = 'edit';
14504 Roo.data.Record.REJECT = 'reject';
14505 Roo.data.Record.COMMIT = 'commit';
14506
14507 Roo.data.Record.prototype = {
14508     /**
14509      * Readonly flag - true if this record has been modified.
14510      * @type Boolean
14511      */
14512     dirty : false,
14513     editing : false,
14514     error: null,
14515     modified: null,
14516
14517     // private
14518     join : function(store){
14519         this.store = store;
14520     },
14521
14522     /**
14523      * Set the named field to the specified value.
14524      * @param {String} name The name of the field to set.
14525      * @param {Object} value The value to set the field to.
14526      */
14527     set : function(name, value){
14528         if(this.data[name] == value){
14529             return;
14530         }
14531         this.dirty = true;
14532         if(!this.modified){
14533             this.modified = {};
14534         }
14535         if(typeof this.modified[name] == 'undefined'){
14536             this.modified[name] = this.data[name];
14537         }
14538         this.data[name] = value;
14539         if(!this.editing && this.store){
14540             this.store.afterEdit(this);
14541         }       
14542     },
14543
14544     /**
14545      * Get the value of the named field.
14546      * @param {String} name The name of the field to get the value of.
14547      * @return {Object} The value of the field.
14548      */
14549     get : function(name){
14550         return this.data[name]; 
14551     },
14552
14553     // private
14554     beginEdit : function(){
14555         this.editing = true;
14556         this.modified = {}; 
14557     },
14558
14559     // private
14560     cancelEdit : function(){
14561         this.editing = false;
14562         delete this.modified;
14563     },
14564
14565     // private
14566     endEdit : function(){
14567         this.editing = false;
14568         if(this.dirty && this.store){
14569             this.store.afterEdit(this);
14570         }
14571     },
14572
14573     /**
14574      * Usually called by the {@link Roo.data.Store} which owns the Record.
14575      * Rejects all changes made to the Record since either creation, or the last commit operation.
14576      * Modified fields are reverted to their original values.
14577      * <p>
14578      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14579      * of reject operations.
14580      */
14581     reject : function(){
14582         var m = this.modified;
14583         for(var n in m){
14584             if(typeof m[n] != "function"){
14585                 this.data[n] = m[n];
14586             }
14587         }
14588         this.dirty = false;
14589         delete this.modified;
14590         this.editing = false;
14591         if(this.store){
14592             this.store.afterReject(this);
14593         }
14594     },
14595
14596     /**
14597      * Usually called by the {@link Roo.data.Store} which owns the Record.
14598      * Commits all changes made to the Record since either creation, or the last commit operation.
14599      * <p>
14600      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14601      * of commit operations.
14602      */
14603     commit : function(){
14604         this.dirty = false;
14605         delete this.modified;
14606         this.editing = false;
14607         if(this.store){
14608             this.store.afterCommit(this);
14609         }
14610     },
14611
14612     // private
14613     hasError : function(){
14614         return this.error != null;
14615     },
14616
14617     // private
14618     clearError : function(){
14619         this.error = null;
14620     },
14621
14622     /**
14623      * Creates a copy of this record.
14624      * @param {String} id (optional) A new record id if you don't want to use this record's id
14625      * @return {Record}
14626      */
14627     copy : function(newId) {
14628         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14629     }
14630 };/*
14631  * Based on:
14632  * Ext JS Library 1.1.1
14633  * Copyright(c) 2006-2007, Ext JS, LLC.
14634  *
14635  * Originally Released Under LGPL - original licence link has changed is not relivant.
14636  *
14637  * Fork - LGPL
14638  * <script type="text/javascript">
14639  */
14640
14641
14642
14643 /**
14644  * @class Roo.data.Store
14645  * @extends Roo.util.Observable
14646  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14647  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14648  * <p>
14649  * 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
14650  * has no knowledge of the format of the data returned by the Proxy.<br>
14651  * <p>
14652  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14653  * instances from the data object. These records are cached and made available through accessor functions.
14654  * @constructor
14655  * Creates a new Store.
14656  * @param {Object} config A config object containing the objects needed for the Store to access data,
14657  * and read the data into Records.
14658  */
14659 Roo.data.Store = function(config){
14660     this.data = new Roo.util.MixedCollection(false);
14661     this.data.getKey = function(o){
14662         return o.id;
14663     };
14664     this.baseParams = {};
14665     // private
14666     this.paramNames = {
14667         "start" : "start",
14668         "limit" : "limit",
14669         "sort" : "sort",
14670         "dir" : "dir",
14671         "multisort" : "_multisort"
14672     };
14673
14674     if(config && config.data){
14675         this.inlineData = config.data;
14676         delete config.data;
14677     }
14678
14679     Roo.apply(this, config);
14680     
14681     if(this.reader){ // reader passed
14682         this.reader = Roo.factory(this.reader, Roo.data);
14683         this.reader.xmodule = this.xmodule || false;
14684         if(!this.recordType){
14685             this.recordType = this.reader.recordType;
14686         }
14687         if(this.reader.onMetaChange){
14688             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14689         }
14690     }
14691
14692     if(this.recordType){
14693         this.fields = this.recordType.prototype.fields;
14694     }
14695     this.modified = [];
14696
14697     this.addEvents({
14698         /**
14699          * @event datachanged
14700          * Fires when the data cache has changed, and a widget which is using this Store
14701          * as a Record cache should refresh its view.
14702          * @param {Store} this
14703          */
14704         datachanged : true,
14705         /**
14706          * @event metachange
14707          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14708          * @param {Store} this
14709          * @param {Object} meta The JSON metadata
14710          */
14711         metachange : true,
14712         /**
14713          * @event add
14714          * Fires when Records have been added to the Store
14715          * @param {Store} this
14716          * @param {Roo.data.Record[]} records The array of Records added
14717          * @param {Number} index The index at which the record(s) were added
14718          */
14719         add : true,
14720         /**
14721          * @event remove
14722          * Fires when a Record has been removed from the Store
14723          * @param {Store} this
14724          * @param {Roo.data.Record} record The Record that was removed
14725          * @param {Number} index The index at which the record was removed
14726          */
14727         remove : true,
14728         /**
14729          * @event update
14730          * Fires when a Record has been updated
14731          * @param {Store} this
14732          * @param {Roo.data.Record} record The Record that was updated
14733          * @param {String} operation The update operation being performed.  Value may be one of:
14734          * <pre><code>
14735  Roo.data.Record.EDIT
14736  Roo.data.Record.REJECT
14737  Roo.data.Record.COMMIT
14738          * </code></pre>
14739          */
14740         update : true,
14741         /**
14742          * @event clear
14743          * Fires when the data cache has been cleared.
14744          * @param {Store} this
14745          */
14746         clear : true,
14747         /**
14748          * @event beforeload
14749          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14750          * the load action will be canceled.
14751          * @param {Store} this
14752          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14753          */
14754         beforeload : true,
14755         /**
14756          * @event beforeloadadd
14757          * Fires after a new set of Records has been loaded.
14758          * @param {Store} this
14759          * @param {Roo.data.Record[]} records The Records that were loaded
14760          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14761          */
14762         beforeloadadd : true,
14763         /**
14764          * @event load
14765          * Fires after a new set of Records has been loaded, before they are added to the store.
14766          * @param {Store} this
14767          * @param {Roo.data.Record[]} records The Records that were loaded
14768          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14769          * @params {Object} return from reader
14770          */
14771         load : true,
14772         /**
14773          * @event loadexception
14774          * Fires if an exception occurs in the Proxy during loading.
14775          * Called with the signature of the Proxy's "loadexception" event.
14776          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14777          * 
14778          * @param {Proxy} 
14779          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14780          * @param {Object} load options 
14781          * @param {Object} jsonData from your request (normally this contains the Exception)
14782          */
14783         loadexception : true
14784     });
14785     
14786     if(this.proxy){
14787         this.proxy = Roo.factory(this.proxy, Roo.data);
14788         this.proxy.xmodule = this.xmodule || false;
14789         this.relayEvents(this.proxy,  ["loadexception"]);
14790     }
14791     this.sortToggle = {};
14792     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14793
14794     Roo.data.Store.superclass.constructor.call(this);
14795
14796     if(this.inlineData){
14797         this.loadData(this.inlineData);
14798         delete this.inlineData;
14799     }
14800 };
14801
14802 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14803      /**
14804     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14805     * without a remote query - used by combo/forms at present.
14806     */
14807     
14808     /**
14809     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14810     */
14811     /**
14812     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14813     */
14814     /**
14815     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14816     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14817     */
14818     /**
14819     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14820     * on any HTTP request
14821     */
14822     /**
14823     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14824     */
14825     /**
14826     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14827     */
14828     multiSort: false,
14829     /**
14830     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14831     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14832     */
14833     remoteSort : false,
14834
14835     /**
14836     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14837      * loaded or when a record is removed. (defaults to false).
14838     */
14839     pruneModifiedRecords : false,
14840
14841     // private
14842     lastOptions : null,
14843
14844     /**
14845      * Add Records to the Store and fires the add event.
14846      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14847      */
14848     add : function(records){
14849         records = [].concat(records);
14850         for(var i = 0, len = records.length; i < len; i++){
14851             records[i].join(this);
14852         }
14853         var index = this.data.length;
14854         this.data.addAll(records);
14855         this.fireEvent("add", this, records, index);
14856     },
14857
14858     /**
14859      * Remove a Record from the Store and fires the remove event.
14860      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14861      */
14862     remove : function(record){
14863         var index = this.data.indexOf(record);
14864         this.data.removeAt(index);
14865  
14866         if(this.pruneModifiedRecords){
14867             this.modified.remove(record);
14868         }
14869         this.fireEvent("remove", this, record, index);
14870     },
14871
14872     /**
14873      * Remove all Records from the Store and fires the clear event.
14874      */
14875     removeAll : function(){
14876         this.data.clear();
14877         if(this.pruneModifiedRecords){
14878             this.modified = [];
14879         }
14880         this.fireEvent("clear", this);
14881     },
14882
14883     /**
14884      * Inserts Records to the Store at the given index and fires the add event.
14885      * @param {Number} index The start index at which to insert the passed Records.
14886      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14887      */
14888     insert : function(index, records){
14889         records = [].concat(records);
14890         for(var i = 0, len = records.length; i < len; i++){
14891             this.data.insert(index, records[i]);
14892             records[i].join(this);
14893         }
14894         this.fireEvent("add", this, records, index);
14895     },
14896
14897     /**
14898      * Get the index within the cache of the passed Record.
14899      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14900      * @return {Number} The index of the passed Record. Returns -1 if not found.
14901      */
14902     indexOf : function(record){
14903         return this.data.indexOf(record);
14904     },
14905
14906     /**
14907      * Get the index within the cache of the Record with the passed id.
14908      * @param {String} id The id of the Record to find.
14909      * @return {Number} The index of the Record. Returns -1 if not found.
14910      */
14911     indexOfId : function(id){
14912         return this.data.indexOfKey(id);
14913     },
14914
14915     /**
14916      * Get the Record with the specified id.
14917      * @param {String} id The id of the Record to find.
14918      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14919      */
14920     getById : function(id){
14921         return this.data.key(id);
14922     },
14923
14924     /**
14925      * Get the Record at the specified index.
14926      * @param {Number} index The index of the Record to find.
14927      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14928      */
14929     getAt : function(index){
14930         return this.data.itemAt(index);
14931     },
14932
14933     /**
14934      * Returns a range of Records between specified indices.
14935      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14936      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14937      * @return {Roo.data.Record[]} An array of Records
14938      */
14939     getRange : function(start, end){
14940         return this.data.getRange(start, end);
14941     },
14942
14943     // private
14944     storeOptions : function(o){
14945         o = Roo.apply({}, o);
14946         delete o.callback;
14947         delete o.scope;
14948         this.lastOptions = o;
14949     },
14950
14951     /**
14952      * Loads the Record cache from the configured Proxy using the configured Reader.
14953      * <p>
14954      * If using remote paging, then the first load call must specify the <em>start</em>
14955      * and <em>limit</em> properties in the options.params property to establish the initial
14956      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14957      * <p>
14958      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14959      * and this call will return before the new data has been loaded. Perform any post-processing
14960      * in a callback function, or in a "load" event handler.</strong>
14961      * <p>
14962      * @param {Object} options An object containing properties which control loading options:<ul>
14963      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14964      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14965      * passed the following arguments:<ul>
14966      * <li>r : Roo.data.Record[]</li>
14967      * <li>options: Options object from the load call</li>
14968      * <li>success: Boolean success indicator</li></ul></li>
14969      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14970      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14971      * </ul>
14972      */
14973     load : function(options){
14974         options = options || {};
14975         if(this.fireEvent("beforeload", this, options) !== false){
14976             this.storeOptions(options);
14977             var p = Roo.apply(options.params || {}, this.baseParams);
14978             // if meta was not loaded from remote source.. try requesting it.
14979             if (!this.reader.metaFromRemote) {
14980                 p._requestMeta = 1;
14981             }
14982             if(this.sortInfo && this.remoteSort){
14983                 var pn = this.paramNames;
14984                 p[pn["sort"]] = this.sortInfo.field;
14985                 p[pn["dir"]] = this.sortInfo.direction;
14986             }
14987             if (this.multiSort) {
14988                 var pn = this.paramNames;
14989                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14990             }
14991             
14992             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14993         }
14994     },
14995
14996     /**
14997      * Reloads the Record cache from the configured Proxy using the configured Reader and
14998      * the options from the last load operation performed.
14999      * @param {Object} options (optional) An object containing properties which may override the options
15000      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15001      * the most recently used options are reused).
15002      */
15003     reload : function(options){
15004         this.load(Roo.applyIf(options||{}, this.lastOptions));
15005     },
15006
15007     // private
15008     // Called as a callback by the Reader during a load operation.
15009     loadRecords : function(o, options, success){
15010         if(!o || success === false){
15011             if(success !== false){
15012                 this.fireEvent("load", this, [], options, o);
15013             }
15014             if(options.callback){
15015                 options.callback.call(options.scope || this, [], options, false);
15016             }
15017             return;
15018         }
15019         // if data returned failure - throw an exception.
15020         if (o.success === false) {
15021             // show a message if no listener is registered.
15022             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15023                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15024             }
15025             // loadmask wil be hooked into this..
15026             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15027             return;
15028         }
15029         var r = o.records, t = o.totalRecords || r.length;
15030         
15031         this.fireEvent("beforeloadadd", this, r, options, o);
15032         
15033         if(!options || options.add !== true){
15034             if(this.pruneModifiedRecords){
15035                 this.modified = [];
15036             }
15037             for(var i = 0, len = r.length; i < len; i++){
15038                 r[i].join(this);
15039             }
15040             if(this.snapshot){
15041                 this.data = this.snapshot;
15042                 delete this.snapshot;
15043             }
15044             this.data.clear();
15045             this.data.addAll(r);
15046             this.totalLength = t;
15047             this.applySort();
15048             this.fireEvent("datachanged", this);
15049         }else{
15050             this.totalLength = Math.max(t, this.data.length+r.length);
15051             this.add(r);
15052         }
15053         
15054         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15055                 
15056             var e = new Roo.data.Record({});
15057
15058             e.set(this.parent.displayField, this.parent.emptyTitle);
15059             e.set(this.parent.valueField, '');
15060
15061             this.insert(0, e);
15062         }
15063             
15064         this.fireEvent("load", this, r, options, o);
15065         if(options.callback){
15066             options.callback.call(options.scope || this, r, options, true);
15067         }
15068     },
15069
15070
15071     /**
15072      * Loads data from a passed data block. A Reader which understands the format of the data
15073      * must have been configured in the constructor.
15074      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15075      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15076      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15077      */
15078     loadData : function(o, append){
15079         var r = this.reader.readRecords(o);
15080         this.loadRecords(r, {add: append}, true);
15081     },
15082     
15083      /**
15084      * using 'cn' the nested child reader read the child array into it's child stores.
15085      * @param {Object} rec The record with a 'children array
15086      */
15087     loadDataFromChildren : function(rec)
15088     {
15089         this.loadData(this.reader.toLoadData(rec));
15090     },
15091     
15092
15093     /**
15094      * Gets the number of cached records.
15095      * <p>
15096      * <em>If using paging, this may not be the total size of the dataset. If the data object
15097      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15098      * the data set size</em>
15099      */
15100     getCount : function(){
15101         return this.data.length || 0;
15102     },
15103
15104     /**
15105      * Gets the total number of records in the dataset as returned by the server.
15106      * <p>
15107      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15108      * the dataset size</em>
15109      */
15110     getTotalCount : function(){
15111         return this.totalLength || 0;
15112     },
15113
15114     /**
15115      * Returns the sort state of the Store as an object with two properties:
15116      * <pre><code>
15117  field {String} The name of the field by which the Records are sorted
15118  direction {String} The sort order, "ASC" or "DESC"
15119      * </code></pre>
15120      */
15121     getSortState : function(){
15122         return this.sortInfo;
15123     },
15124
15125     // private
15126     applySort : function(){
15127         if(this.sortInfo && !this.remoteSort){
15128             var s = this.sortInfo, f = s.field;
15129             var st = this.fields.get(f).sortType;
15130             var fn = function(r1, r2){
15131                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15132                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15133             };
15134             this.data.sort(s.direction, fn);
15135             if(this.snapshot && this.snapshot != this.data){
15136                 this.snapshot.sort(s.direction, fn);
15137             }
15138         }
15139     },
15140
15141     /**
15142      * Sets the default sort column and order to be used by the next load operation.
15143      * @param {String} fieldName The name of the field to sort by.
15144      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15145      */
15146     setDefaultSort : function(field, dir){
15147         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15148     },
15149
15150     /**
15151      * Sort the Records.
15152      * If remote sorting is used, the sort is performed on the server, and the cache is
15153      * reloaded. If local sorting is used, the cache is sorted internally.
15154      * @param {String} fieldName The name of the field to sort by.
15155      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15156      */
15157     sort : function(fieldName, dir){
15158         var f = this.fields.get(fieldName);
15159         if(!dir){
15160             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15161             
15162             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15163                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15164             }else{
15165                 dir = f.sortDir;
15166             }
15167         }
15168         this.sortToggle[f.name] = dir;
15169         this.sortInfo = {field: f.name, direction: dir};
15170         if(!this.remoteSort){
15171             this.applySort();
15172             this.fireEvent("datachanged", this);
15173         }else{
15174             this.load(this.lastOptions);
15175         }
15176     },
15177
15178     /**
15179      * Calls the specified function for each of the Records in the cache.
15180      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15181      * Returning <em>false</em> aborts and exits the iteration.
15182      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15183      */
15184     each : function(fn, scope){
15185         this.data.each(fn, scope);
15186     },
15187
15188     /**
15189      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15190      * (e.g., during paging).
15191      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15192      */
15193     getModifiedRecords : function(){
15194         return this.modified;
15195     },
15196
15197     // private
15198     createFilterFn : function(property, value, anyMatch){
15199         if(!value.exec){ // not a regex
15200             value = String(value);
15201             if(value.length == 0){
15202                 return false;
15203             }
15204             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15205         }
15206         return function(r){
15207             return value.test(r.data[property]);
15208         };
15209     },
15210
15211     /**
15212      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15213      * @param {String} property A field on your records
15214      * @param {Number} start The record index to start at (defaults to 0)
15215      * @param {Number} end The last record index to include (defaults to length - 1)
15216      * @return {Number} The sum
15217      */
15218     sum : function(property, start, end){
15219         var rs = this.data.items, v = 0;
15220         start = start || 0;
15221         end = (end || end === 0) ? end : rs.length-1;
15222
15223         for(var i = start; i <= end; i++){
15224             v += (rs[i].data[property] || 0);
15225         }
15226         return v;
15227     },
15228
15229     /**
15230      * Filter the records by a specified property.
15231      * @param {String} field A field on your records
15232      * @param {String/RegExp} value Either a string that the field
15233      * should start with or a RegExp to test against the field
15234      * @param {Boolean} anyMatch True to match any part not just the beginning
15235      */
15236     filter : function(property, value, anyMatch){
15237         var fn = this.createFilterFn(property, value, anyMatch);
15238         return fn ? this.filterBy(fn) : this.clearFilter();
15239     },
15240
15241     /**
15242      * Filter by a function. The specified function will be called with each
15243      * record in this data source. If the function returns true the record is included,
15244      * otherwise it is filtered.
15245      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15246      * @param {Object} scope (optional) The scope of the function (defaults to this)
15247      */
15248     filterBy : function(fn, scope){
15249         this.snapshot = this.snapshot || this.data;
15250         this.data = this.queryBy(fn, scope||this);
15251         this.fireEvent("datachanged", this);
15252     },
15253
15254     /**
15255      * Query the records by a specified property.
15256      * @param {String} field A field on your records
15257      * @param {String/RegExp} value Either a string that the field
15258      * should start with or a RegExp to test against the field
15259      * @param {Boolean} anyMatch True to match any part not just the beginning
15260      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15261      */
15262     query : function(property, value, anyMatch){
15263         var fn = this.createFilterFn(property, value, anyMatch);
15264         return fn ? this.queryBy(fn) : this.data.clone();
15265     },
15266
15267     /**
15268      * Query by a function. The specified function will be called with each
15269      * record in this data source. If the function returns true the record is included
15270      * in the results.
15271      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15272      * @param {Object} scope (optional) The scope of the function (defaults to this)
15273       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15274      **/
15275     queryBy : function(fn, scope){
15276         var data = this.snapshot || this.data;
15277         return data.filterBy(fn, scope||this);
15278     },
15279
15280     /**
15281      * Collects unique values for a particular dataIndex from this store.
15282      * @param {String} dataIndex The property to collect
15283      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15284      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15285      * @return {Array} An array of the unique values
15286      **/
15287     collect : function(dataIndex, allowNull, bypassFilter){
15288         var d = (bypassFilter === true && this.snapshot) ?
15289                 this.snapshot.items : this.data.items;
15290         var v, sv, r = [], l = {};
15291         for(var i = 0, len = d.length; i < len; i++){
15292             v = d[i].data[dataIndex];
15293             sv = String(v);
15294             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15295                 l[sv] = true;
15296                 r[r.length] = v;
15297             }
15298         }
15299         return r;
15300     },
15301
15302     /**
15303      * Revert to a view of the Record cache with no filtering applied.
15304      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15305      */
15306     clearFilter : function(suppressEvent){
15307         if(this.snapshot && this.snapshot != this.data){
15308             this.data = this.snapshot;
15309             delete this.snapshot;
15310             if(suppressEvent !== true){
15311                 this.fireEvent("datachanged", this);
15312             }
15313         }
15314     },
15315
15316     // private
15317     afterEdit : function(record){
15318         if(this.modified.indexOf(record) == -1){
15319             this.modified.push(record);
15320         }
15321         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15322     },
15323     
15324     // private
15325     afterReject : function(record){
15326         this.modified.remove(record);
15327         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15328     },
15329
15330     // private
15331     afterCommit : function(record){
15332         this.modified.remove(record);
15333         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15334     },
15335
15336     /**
15337      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15338      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15339      */
15340     commitChanges : function(){
15341         var m = this.modified.slice(0);
15342         this.modified = [];
15343         for(var i = 0, len = m.length; i < len; i++){
15344             m[i].commit();
15345         }
15346     },
15347
15348     /**
15349      * Cancel outstanding changes on all changed records.
15350      */
15351     rejectChanges : function(){
15352         var m = this.modified.slice(0);
15353         this.modified = [];
15354         for(var i = 0, len = m.length; i < len; i++){
15355             m[i].reject();
15356         }
15357     },
15358
15359     onMetaChange : function(meta, rtype, o){
15360         this.recordType = rtype;
15361         this.fields = rtype.prototype.fields;
15362         delete this.snapshot;
15363         this.sortInfo = meta.sortInfo || this.sortInfo;
15364         this.modified = [];
15365         this.fireEvent('metachange', this, this.reader.meta);
15366     },
15367     
15368     moveIndex : function(data, type)
15369     {
15370         var index = this.indexOf(data);
15371         
15372         var newIndex = index + type;
15373         
15374         this.remove(data);
15375         
15376         this.insert(newIndex, data);
15377         
15378     }
15379 });/*
15380  * Based on:
15381  * Ext JS Library 1.1.1
15382  * Copyright(c) 2006-2007, Ext JS, LLC.
15383  *
15384  * Originally Released Under LGPL - original licence link has changed is not relivant.
15385  *
15386  * Fork - LGPL
15387  * <script type="text/javascript">
15388  */
15389
15390 /**
15391  * @class Roo.data.SimpleStore
15392  * @extends Roo.data.Store
15393  * Small helper class to make creating Stores from Array data easier.
15394  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15395  * @cfg {Array} fields An array of field definition objects, or field name strings.
15396  * @cfg {Object} an existing reader (eg. copied from another store)
15397  * @cfg {Array} data The multi-dimensional array of data
15398  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15399  * @cfg {Roo.data.Reader} reader  [not-required] 
15400  * @constructor
15401  * @param {Object} config
15402  */
15403 Roo.data.SimpleStore = function(config)
15404 {
15405     Roo.data.SimpleStore.superclass.constructor.call(this, {
15406         isLocal : true,
15407         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15408                 id: config.id
15409             },
15410             Roo.data.Record.create(config.fields)
15411         ),
15412         proxy : new Roo.data.MemoryProxy(config.data)
15413     });
15414     this.load();
15415 };
15416 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15417  * Based on:
15418  * Ext JS Library 1.1.1
15419  * Copyright(c) 2006-2007, Ext JS, LLC.
15420  *
15421  * Originally Released Under LGPL - original licence link has changed is not relivant.
15422  *
15423  * Fork - LGPL
15424  * <script type="text/javascript">
15425  */
15426
15427 /**
15428 /**
15429  * @extends Roo.data.Store
15430  * @class Roo.data.JsonStore
15431  * Small helper class to make creating Stores for JSON data easier. <br/>
15432 <pre><code>
15433 var store = new Roo.data.JsonStore({
15434     url: 'get-images.php',
15435     root: 'images',
15436     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15437 });
15438 </code></pre>
15439  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15440  * JsonReader and HttpProxy (unless inline data is provided).</b>
15441  * @cfg {Array} fields An array of field definition objects, or field name strings.
15442  * @constructor
15443  * @param {Object} config
15444  */
15445 Roo.data.JsonStore = function(c){
15446     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15447         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15448         reader: new Roo.data.JsonReader(c, c.fields)
15449     }));
15450 };
15451 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15452  * Based on:
15453  * Ext JS Library 1.1.1
15454  * Copyright(c) 2006-2007, Ext JS, LLC.
15455  *
15456  * Originally Released Under LGPL - original licence link has changed is not relivant.
15457  *
15458  * Fork - LGPL
15459  * <script type="text/javascript">
15460  */
15461
15462  
15463 Roo.data.Field = function(config){
15464     if(typeof config == "string"){
15465         config = {name: config};
15466     }
15467     Roo.apply(this, config);
15468     
15469     if(!this.type){
15470         this.type = "auto";
15471     }
15472     
15473     var st = Roo.data.SortTypes;
15474     // named sortTypes are supported, here we look them up
15475     if(typeof this.sortType == "string"){
15476         this.sortType = st[this.sortType];
15477     }
15478     
15479     // set default sortType for strings and dates
15480     if(!this.sortType){
15481         switch(this.type){
15482             case "string":
15483                 this.sortType = st.asUCString;
15484                 break;
15485             case "date":
15486                 this.sortType = st.asDate;
15487                 break;
15488             default:
15489                 this.sortType = st.none;
15490         }
15491     }
15492
15493     // define once
15494     var stripRe = /[\$,%]/g;
15495
15496     // prebuilt conversion function for this field, instead of
15497     // switching every time we're reading a value
15498     if(!this.convert){
15499         var cv, dateFormat = this.dateFormat;
15500         switch(this.type){
15501             case "":
15502             case "auto":
15503             case undefined:
15504                 cv = function(v){ return v; };
15505                 break;
15506             case "string":
15507                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15508                 break;
15509             case "int":
15510                 cv = function(v){
15511                     return v !== undefined && v !== null && v !== '' ?
15512                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15513                     };
15514                 break;
15515             case "float":
15516                 cv = function(v){
15517                     return v !== undefined && v !== null && v !== '' ?
15518                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15519                     };
15520                 break;
15521             case "bool":
15522             case "boolean":
15523                 cv = function(v){ return v === true || v === "true" || v == 1; };
15524                 break;
15525             case "date":
15526                 cv = function(v){
15527                     if(!v){
15528                         return '';
15529                     }
15530                     if(v instanceof Date){
15531                         return v;
15532                     }
15533                     if(dateFormat){
15534                         if(dateFormat == "timestamp"){
15535                             return new Date(v*1000);
15536                         }
15537                         return Date.parseDate(v, dateFormat);
15538                     }
15539                     var parsed = Date.parse(v);
15540                     return parsed ? new Date(parsed) : null;
15541                 };
15542              break;
15543             
15544         }
15545         this.convert = cv;
15546     }
15547 };
15548
15549 Roo.data.Field.prototype = {
15550     dateFormat: null,
15551     defaultValue: "",
15552     mapping: null,
15553     sortType : null,
15554     sortDir : "ASC"
15555 };/*
15556  * Based on:
15557  * Ext JS Library 1.1.1
15558  * Copyright(c) 2006-2007, Ext JS, LLC.
15559  *
15560  * Originally Released Under LGPL - original licence link has changed is not relivant.
15561  *
15562  * Fork - LGPL
15563  * <script type="text/javascript">
15564  */
15565  
15566 // Base class for reading structured data from a data source.  This class is intended to be
15567 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15568
15569 /**
15570  * @class Roo.data.DataReader
15571  * @abstract
15572  * Base class for reading structured data from a data source.  This class is intended to be
15573  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15574  */
15575
15576 Roo.data.DataReader = function(meta, recordType){
15577     
15578     this.meta = meta;
15579     
15580     this.recordType = recordType instanceof Array ? 
15581         Roo.data.Record.create(recordType) : recordType;
15582 };
15583
15584 Roo.data.DataReader.prototype = {
15585     
15586     
15587     readerType : 'Data',
15588      /**
15589      * Create an empty record
15590      * @param {Object} data (optional) - overlay some values
15591      * @return {Roo.data.Record} record created.
15592      */
15593     newRow :  function(d) {
15594         var da =  {};
15595         this.recordType.prototype.fields.each(function(c) {
15596             switch( c.type) {
15597                 case 'int' : da[c.name] = 0; break;
15598                 case 'date' : da[c.name] = new Date(); break;
15599                 case 'float' : da[c.name] = 0.0; break;
15600                 case 'boolean' : da[c.name] = false; break;
15601                 default : da[c.name] = ""; break;
15602             }
15603             
15604         });
15605         return new this.recordType(Roo.apply(da, d));
15606     }
15607     
15608     
15609 };/*
15610  * Based on:
15611  * Ext JS Library 1.1.1
15612  * Copyright(c) 2006-2007, Ext JS, LLC.
15613  *
15614  * Originally Released Under LGPL - original licence link has changed is not relivant.
15615  *
15616  * Fork - LGPL
15617  * <script type="text/javascript">
15618  */
15619
15620 /**
15621  * @class Roo.data.DataProxy
15622  * @extends Roo.data.Observable
15623  * @abstract
15624  * This class is an abstract base class for implementations which provide retrieval of
15625  * unformatted data objects.<br>
15626  * <p>
15627  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15628  * (of the appropriate type which knows how to parse the data object) to provide a block of
15629  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15630  * <p>
15631  * Custom implementations must implement the load method as described in
15632  * {@link Roo.data.HttpProxy#load}.
15633  */
15634 Roo.data.DataProxy = function(){
15635     this.addEvents({
15636         /**
15637          * @event beforeload
15638          * Fires before a network request is made to retrieve a data object.
15639          * @param {Object} This DataProxy object.
15640          * @param {Object} params The params parameter to the load function.
15641          */
15642         beforeload : true,
15643         /**
15644          * @event load
15645          * Fires before the load method's callback is called.
15646          * @param {Object} This DataProxy object.
15647          * @param {Object} o The data object.
15648          * @param {Object} arg The callback argument object passed to the load function.
15649          */
15650         load : true,
15651         /**
15652          * @event loadexception
15653          * Fires if an Exception occurs during data retrieval.
15654          * @param {Object} This DataProxy object.
15655          * @param {Object} o The data object.
15656          * @param {Object} arg The callback argument object passed to the load function.
15657          * @param {Object} e The Exception.
15658          */
15659         loadexception : true
15660     });
15661     Roo.data.DataProxy.superclass.constructor.call(this);
15662 };
15663
15664 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15665
15666     /**
15667      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15668      */
15669 /*
15670  * Based on:
15671  * Ext JS Library 1.1.1
15672  * Copyright(c) 2006-2007, Ext JS, LLC.
15673  *
15674  * Originally Released Under LGPL - original licence link has changed is not relivant.
15675  *
15676  * Fork - LGPL
15677  * <script type="text/javascript">
15678  */
15679 /**
15680  * @class Roo.data.MemoryProxy
15681  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15682  * to the Reader when its load method is called.
15683  * @constructor
15684  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15685  */
15686 Roo.data.MemoryProxy = function(data){
15687     if (data.data) {
15688         data = data.data;
15689     }
15690     Roo.data.MemoryProxy.superclass.constructor.call(this);
15691     this.data = data;
15692 };
15693
15694 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15695     
15696     /**
15697      * Load data from the requested source (in this case an in-memory
15698      * data object passed to the constructor), read the data object into
15699      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15700      * process that block using the passed callback.
15701      * @param {Object} params This parameter is not used by the MemoryProxy class.
15702      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15703      * object into a block of Roo.data.Records.
15704      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15705      * The function must be passed <ul>
15706      * <li>The Record block object</li>
15707      * <li>The "arg" argument from the load function</li>
15708      * <li>A boolean success indicator</li>
15709      * </ul>
15710      * @param {Object} scope The scope in which to call the callback
15711      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15712      */
15713     load : function(params, reader, callback, scope, arg){
15714         params = params || {};
15715         var result;
15716         try {
15717             result = reader.readRecords(params.data ? params.data :this.data);
15718         }catch(e){
15719             this.fireEvent("loadexception", this, arg, null, e);
15720             callback.call(scope, null, arg, false);
15721             return;
15722         }
15723         callback.call(scope, result, arg, true);
15724     },
15725     
15726     // private
15727     update : function(params, records){
15728         
15729     }
15730 });/*
15731  * Based on:
15732  * Ext JS Library 1.1.1
15733  * Copyright(c) 2006-2007, Ext JS, LLC.
15734  *
15735  * Originally Released Under LGPL - original licence link has changed is not relivant.
15736  *
15737  * Fork - LGPL
15738  * <script type="text/javascript">
15739  */
15740 /**
15741  * @class Roo.data.HttpProxy
15742  * @extends Roo.data.DataProxy
15743  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15744  * configured to reference a certain URL.<br><br>
15745  * <p>
15746  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15747  * from which the running page was served.<br><br>
15748  * <p>
15749  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15750  * <p>
15751  * Be aware that to enable the browser to parse an XML document, the server must set
15752  * the Content-Type header in the HTTP response to "text/xml".
15753  * @constructor
15754  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15755  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15756  * will be used to make the request.
15757  */
15758 Roo.data.HttpProxy = function(conn){
15759     Roo.data.HttpProxy.superclass.constructor.call(this);
15760     // is conn a conn config or a real conn?
15761     this.conn = conn;
15762     this.useAjax = !conn || !conn.events;
15763   
15764 };
15765
15766 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15767     // thse are take from connection...
15768     
15769     /**
15770      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15771      */
15772     /**
15773      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15774      * extra parameters to each request made by this object. (defaults to undefined)
15775      */
15776     /**
15777      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15778      *  to each request made by this object. (defaults to undefined)
15779      */
15780     /**
15781      * @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)
15782      */
15783     /**
15784      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15785      */
15786      /**
15787      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15788      * @type Boolean
15789      */
15790   
15791
15792     /**
15793      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15794      * @type Boolean
15795      */
15796     /**
15797      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15798      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15799      * a finer-grained basis than the DataProxy events.
15800      */
15801     getConnection : function(){
15802         return this.useAjax ? Roo.Ajax : this.conn;
15803     },
15804
15805     /**
15806      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15807      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15808      * process that block using the passed callback.
15809      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15810      * for the request to the remote server.
15811      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15812      * object into a block of Roo.data.Records.
15813      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15814      * The function must be passed <ul>
15815      * <li>The Record block object</li>
15816      * <li>The "arg" argument from the load function</li>
15817      * <li>A boolean success indicator</li>
15818      * </ul>
15819      * @param {Object} scope The scope in which to call the callback
15820      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15821      */
15822     load : function(params, reader, callback, scope, arg){
15823         if(this.fireEvent("beforeload", this, params) !== false){
15824             var  o = {
15825                 params : params || {},
15826                 request: {
15827                     callback : callback,
15828                     scope : scope,
15829                     arg : arg
15830                 },
15831                 reader: reader,
15832                 callback : this.loadResponse,
15833                 scope: this
15834             };
15835             if(this.useAjax){
15836                 Roo.applyIf(o, this.conn);
15837                 if(this.activeRequest){
15838                     Roo.Ajax.abort(this.activeRequest);
15839                 }
15840                 this.activeRequest = Roo.Ajax.request(o);
15841             }else{
15842                 this.conn.request(o);
15843             }
15844         }else{
15845             callback.call(scope||this, null, arg, false);
15846         }
15847     },
15848
15849     // private
15850     loadResponse : function(o, success, response){
15851         delete this.activeRequest;
15852         if(!success){
15853             this.fireEvent("loadexception", this, o, response);
15854             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15855             return;
15856         }
15857         var result;
15858         try {
15859             result = o.reader.read(response);
15860         }catch(e){
15861             this.fireEvent("loadexception", this, o, response, e);
15862             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15863             return;
15864         }
15865         
15866         this.fireEvent("load", this, o, o.request.arg);
15867         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15868     },
15869
15870     // private
15871     update : function(dataSet){
15872
15873     },
15874
15875     // private
15876     updateResponse : function(dataSet){
15877
15878     }
15879 });/*
15880  * Based on:
15881  * Ext JS Library 1.1.1
15882  * Copyright(c) 2006-2007, Ext JS, LLC.
15883  *
15884  * Originally Released Under LGPL - original licence link has changed is not relivant.
15885  *
15886  * Fork - LGPL
15887  * <script type="text/javascript">
15888  */
15889
15890 /**
15891  * @class Roo.data.ScriptTagProxy
15892  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15893  * other than the originating domain of the running page.<br><br>
15894  * <p>
15895  * <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
15896  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15897  * <p>
15898  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15899  * source code that is used as the source inside a &lt;script> tag.<br><br>
15900  * <p>
15901  * In order for the browser to process the returned data, the server must wrap the data object
15902  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15903  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15904  * depending on whether the callback name was passed:
15905  * <p>
15906  * <pre><code>
15907 boolean scriptTag = false;
15908 String cb = request.getParameter("callback");
15909 if (cb != null) {
15910     scriptTag = true;
15911     response.setContentType("text/javascript");
15912 } else {
15913     response.setContentType("application/x-json");
15914 }
15915 Writer out = response.getWriter();
15916 if (scriptTag) {
15917     out.write(cb + "(");
15918 }
15919 out.print(dataBlock.toJsonString());
15920 if (scriptTag) {
15921     out.write(");");
15922 }
15923 </pre></code>
15924  *
15925  * @constructor
15926  * @param {Object} config A configuration object.
15927  */
15928 Roo.data.ScriptTagProxy = function(config){
15929     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15930     Roo.apply(this, config);
15931     this.head = document.getElementsByTagName("head")[0];
15932 };
15933
15934 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15935
15936 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15937     /**
15938      * @cfg {String} url The URL from which to request the data object.
15939      */
15940     /**
15941      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15942      */
15943     timeout : 30000,
15944     /**
15945      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15946      * the server the name of the callback function set up by the load call to process the returned data object.
15947      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15948      * javascript output which calls this named function passing the data object as its only parameter.
15949      */
15950     callbackParam : "callback",
15951     /**
15952      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15953      * name to the request.
15954      */
15955     nocache : true,
15956
15957     /**
15958      * Load data from the configured URL, read the data object into
15959      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15960      * process that block using the passed callback.
15961      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15962      * for the request to the remote server.
15963      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15964      * object into a block of Roo.data.Records.
15965      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15966      * The function must be passed <ul>
15967      * <li>The Record block object</li>
15968      * <li>The "arg" argument from the load function</li>
15969      * <li>A boolean success indicator</li>
15970      * </ul>
15971      * @param {Object} scope The scope in which to call the callback
15972      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15973      */
15974     load : function(params, reader, callback, scope, arg){
15975         if(this.fireEvent("beforeload", this, params) !== false){
15976
15977             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15978
15979             var url = this.url;
15980             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15981             if(this.nocache){
15982                 url += "&_dc=" + (new Date().getTime());
15983             }
15984             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15985             var trans = {
15986                 id : transId,
15987                 cb : "stcCallback"+transId,
15988                 scriptId : "stcScript"+transId,
15989                 params : params,
15990                 arg : arg,
15991                 url : url,
15992                 callback : callback,
15993                 scope : scope,
15994                 reader : reader
15995             };
15996             var conn = this;
15997
15998             window[trans.cb] = function(o){
15999                 conn.handleResponse(o, trans);
16000             };
16001
16002             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16003
16004             if(this.autoAbort !== false){
16005                 this.abort();
16006             }
16007
16008             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16009
16010             var script = document.createElement("script");
16011             script.setAttribute("src", url);
16012             script.setAttribute("type", "text/javascript");
16013             script.setAttribute("id", trans.scriptId);
16014             this.head.appendChild(script);
16015
16016             this.trans = trans;
16017         }else{
16018             callback.call(scope||this, null, arg, false);
16019         }
16020     },
16021
16022     // private
16023     isLoading : function(){
16024         return this.trans ? true : false;
16025     },
16026
16027     /**
16028      * Abort the current server request.
16029      */
16030     abort : function(){
16031         if(this.isLoading()){
16032             this.destroyTrans(this.trans);
16033         }
16034     },
16035
16036     // private
16037     destroyTrans : function(trans, isLoaded){
16038         this.head.removeChild(document.getElementById(trans.scriptId));
16039         clearTimeout(trans.timeoutId);
16040         if(isLoaded){
16041             window[trans.cb] = undefined;
16042             try{
16043                 delete window[trans.cb];
16044             }catch(e){}
16045         }else{
16046             // if hasn't been loaded, wait for load to remove it to prevent script error
16047             window[trans.cb] = function(){
16048                 window[trans.cb] = undefined;
16049                 try{
16050                     delete window[trans.cb];
16051                 }catch(e){}
16052             };
16053         }
16054     },
16055
16056     // private
16057     handleResponse : function(o, trans){
16058         this.trans = false;
16059         this.destroyTrans(trans, true);
16060         var result;
16061         try {
16062             result = trans.reader.readRecords(o);
16063         }catch(e){
16064             this.fireEvent("loadexception", this, o, trans.arg, e);
16065             trans.callback.call(trans.scope||window, null, trans.arg, false);
16066             return;
16067         }
16068         this.fireEvent("load", this, o, trans.arg);
16069         trans.callback.call(trans.scope||window, result, trans.arg, true);
16070     },
16071
16072     // private
16073     handleFailure : function(trans){
16074         this.trans = false;
16075         this.destroyTrans(trans, false);
16076         this.fireEvent("loadexception", this, null, trans.arg);
16077         trans.callback.call(trans.scope||window, null, trans.arg, false);
16078     }
16079 });/*
16080  * Based on:
16081  * Ext JS Library 1.1.1
16082  * Copyright(c) 2006-2007, Ext JS, LLC.
16083  *
16084  * Originally Released Under LGPL - original licence link has changed is not relivant.
16085  *
16086  * Fork - LGPL
16087  * <script type="text/javascript">
16088  */
16089
16090 /**
16091  * @class Roo.data.JsonReader
16092  * @extends Roo.data.DataReader
16093  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16094  * based on mappings in a provided Roo.data.Record constructor.
16095  * 
16096  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16097  * in the reply previously. 
16098  * 
16099  * <p>
16100  * Example code:
16101  * <pre><code>
16102 var RecordDef = Roo.data.Record.create([
16103     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16104     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16105 ]);
16106 var myReader = new Roo.data.JsonReader({
16107     totalProperty: "results",    // The property which contains the total dataset size (optional)
16108     root: "rows",                // The property which contains an Array of row objects
16109     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16110 }, RecordDef);
16111 </code></pre>
16112  * <p>
16113  * This would consume a JSON file like this:
16114  * <pre><code>
16115 { 'results': 2, 'rows': [
16116     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16117     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16118 }
16119 </code></pre>
16120  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16121  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16122  * paged from the remote server.
16123  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16124  * @cfg {String} root name of the property which contains the Array of row objects.
16125  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16126  * @cfg {Array} fields Array of field definition objects
16127  * @constructor
16128  * Create a new JsonReader
16129  * @param {Object} meta Metadata configuration options
16130  * @param {Object} recordType Either an Array of field definition objects,
16131  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16132  */
16133 Roo.data.JsonReader = function(meta, recordType){
16134     
16135     meta = meta || {};
16136     // set some defaults:
16137     Roo.applyIf(meta, {
16138         totalProperty: 'total',
16139         successProperty : 'success',
16140         root : 'data',
16141         id : 'id'
16142     });
16143     
16144     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16145 };
16146 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16147     
16148     readerType : 'Json',
16149     
16150     /**
16151      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16152      * Used by Store query builder to append _requestMeta to params.
16153      * 
16154      */
16155     metaFromRemote : false,
16156     /**
16157      * This method is only used by a DataProxy which has retrieved data from a remote server.
16158      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16159      * @return {Object} data A data block which is used by an Roo.data.Store object as
16160      * a cache of Roo.data.Records.
16161      */
16162     read : function(response){
16163         var json = response.responseText;
16164        
16165         var o = /* eval:var:o */ eval("("+json+")");
16166         if(!o) {
16167             throw {message: "JsonReader.read: Json object not found"};
16168         }
16169         
16170         if(o.metaData){
16171             
16172             delete this.ef;
16173             this.metaFromRemote = true;
16174             this.meta = o.metaData;
16175             this.recordType = Roo.data.Record.create(o.metaData.fields);
16176             this.onMetaChange(this.meta, this.recordType, o);
16177         }
16178         return this.readRecords(o);
16179     },
16180
16181     // private function a store will implement
16182     onMetaChange : function(meta, recordType, o){
16183
16184     },
16185
16186     /**
16187          * @ignore
16188          */
16189     simpleAccess: function(obj, subsc) {
16190         return obj[subsc];
16191     },
16192
16193         /**
16194          * @ignore
16195          */
16196     getJsonAccessor: function(){
16197         var re = /[\[\.]/;
16198         return function(expr) {
16199             try {
16200                 return(re.test(expr))
16201                     ? new Function("obj", "return obj." + expr)
16202                     : function(obj){
16203                         return obj[expr];
16204                     };
16205             } catch(e){}
16206             return Roo.emptyFn;
16207         };
16208     }(),
16209
16210     /**
16211      * Create a data block containing Roo.data.Records from an XML document.
16212      * @param {Object} o An object which contains an Array of row objects in the property specified
16213      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16214      * which contains the total size of the dataset.
16215      * @return {Object} data A data block which is used by an Roo.data.Store object as
16216      * a cache of Roo.data.Records.
16217      */
16218     readRecords : function(o){
16219         /**
16220          * After any data loads, the raw JSON data is available for further custom processing.
16221          * @type Object
16222          */
16223         this.o = o;
16224         var s = this.meta, Record = this.recordType,
16225             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16226
16227 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16228         if (!this.ef) {
16229             if(s.totalProperty) {
16230                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16231                 }
16232                 if(s.successProperty) {
16233                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16234                 }
16235                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16236                 if (s.id) {
16237                         var g = this.getJsonAccessor(s.id);
16238                         this.getId = function(rec) {
16239                                 var r = g(rec);  
16240                                 return (r === undefined || r === "") ? null : r;
16241                         };
16242                 } else {
16243                         this.getId = function(){return null;};
16244                 }
16245             this.ef = [];
16246             for(var jj = 0; jj < fl; jj++){
16247                 f = fi[jj];
16248                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16249                 this.ef[jj] = this.getJsonAccessor(map);
16250             }
16251         }
16252
16253         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16254         if(s.totalProperty){
16255             var vt = parseInt(this.getTotal(o), 10);
16256             if(!isNaN(vt)){
16257                 totalRecords = vt;
16258             }
16259         }
16260         if(s.successProperty){
16261             var vs = this.getSuccess(o);
16262             if(vs === false || vs === 'false'){
16263                 success = false;
16264             }
16265         }
16266         var records = [];
16267         for(var i = 0; i < c; i++){
16268                 var n = root[i];
16269             var values = {};
16270             var id = this.getId(n);
16271             for(var j = 0; j < fl; j++){
16272                 f = fi[j];
16273             var v = this.ef[j](n);
16274             if (!f.convert) {
16275                 Roo.log('missing convert for ' + f.name);
16276                 Roo.log(f);
16277                 continue;
16278             }
16279             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16280             }
16281             var record = new Record(values, id);
16282             record.json = n;
16283             records[i] = record;
16284         }
16285         return {
16286             raw : o,
16287             success : success,
16288             records : records,
16289             totalRecords : totalRecords
16290         };
16291     },
16292     // used when loading children.. @see loadDataFromChildren
16293     toLoadData: function(rec)
16294     {
16295         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16296         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16297         return { data : data, total : data.length };
16298         
16299     }
16300 });/*
16301  * Based on:
16302  * Ext JS Library 1.1.1
16303  * Copyright(c) 2006-2007, Ext JS, LLC.
16304  *
16305  * Originally Released Under LGPL - original licence link has changed is not relivant.
16306  *
16307  * Fork - LGPL
16308  * <script type="text/javascript">
16309  */
16310
16311 /**
16312  * @class Roo.data.ArrayReader
16313  * @extends Roo.data.DataReader
16314  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16315  * Each element of that Array represents a row of data fields. The
16316  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16317  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16318  * <p>
16319  * Example code:.
16320  * <pre><code>
16321 var RecordDef = Roo.data.Record.create([
16322     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16323     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16324 ]);
16325 var myReader = new Roo.data.ArrayReader({
16326     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16327 }, RecordDef);
16328 </code></pre>
16329  * <p>
16330  * This would consume an Array like this:
16331  * <pre><code>
16332 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16333   </code></pre>
16334  
16335  * @constructor
16336  * Create a new JsonReader
16337  * @param {Object} meta Metadata configuration options.
16338  * @param {Object|Array} recordType Either an Array of field definition objects
16339  * 
16340  * @cfg {Array} fields Array of field definition objects
16341  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16342  * as specified to {@link Roo.data.Record#create},
16343  * or an {@link Roo.data.Record} object
16344  *
16345  * 
16346  * created using {@link Roo.data.Record#create}.
16347  */
16348 Roo.data.ArrayReader = function(meta, recordType)
16349 {    
16350     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16351 };
16352
16353 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16354     
16355       /**
16356      * Create a data block containing Roo.data.Records from an XML document.
16357      * @param {Object} o An Array of row objects which represents the dataset.
16358      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16359      * a cache of Roo.data.Records.
16360      */
16361     readRecords : function(o)
16362     {
16363         var sid = this.meta ? this.meta.id : null;
16364         var recordType = this.recordType, fields = recordType.prototype.fields;
16365         var records = [];
16366         var root = o;
16367         for(var i = 0; i < root.length; i++){
16368             var n = root[i];
16369             var values = {};
16370             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16371             for(var j = 0, jlen = fields.length; j < jlen; j++){
16372                 var f = fields.items[j];
16373                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16374                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16375                 v = f.convert(v);
16376                 values[f.name] = v;
16377             }
16378             var record = new recordType(values, id);
16379             record.json = n;
16380             records[records.length] = record;
16381         }
16382         return {
16383             records : records,
16384             totalRecords : records.length
16385         };
16386     },
16387     // used when loading children.. @see loadDataFromChildren
16388     toLoadData: function(rec)
16389     {
16390         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16391         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16392         
16393     }
16394     
16395     
16396 });/*
16397  * - LGPL
16398  * * 
16399  */
16400
16401 /**
16402  * @class Roo.bootstrap.ComboBox
16403  * @extends Roo.bootstrap.TriggerField
16404  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16405  * @cfg {Boolean} append (true|false) default false
16406  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16407  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16408  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16409  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16410  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16411  * @cfg {Boolean} animate default true
16412  * @cfg {Boolean} emptyResultText only for touch device
16413  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16414  * @cfg {String} emptyTitle default ''
16415  * @cfg {Number} width fixed with? experimental
16416  * @constructor
16417  * Create a new ComboBox.
16418  * @param {Object} config Configuration options
16419  */
16420 Roo.bootstrap.ComboBox = function(config){
16421     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16422     this.addEvents({
16423         /**
16424          * @event expand
16425          * Fires when the dropdown list is expanded
16426         * @param {Roo.bootstrap.ComboBox} combo This combo box
16427         */
16428         'expand' : true,
16429         /**
16430          * @event collapse
16431          * Fires when the dropdown list is collapsed
16432         * @param {Roo.bootstrap.ComboBox} combo This combo box
16433         */
16434         'collapse' : true,
16435         /**
16436          * @event beforeselect
16437          * Fires before a list item is selected. Return false to cancel the selection.
16438         * @param {Roo.bootstrap.ComboBox} combo This combo box
16439         * @param {Roo.data.Record} record The data record returned from the underlying store
16440         * @param {Number} index The index of the selected item in the dropdown list
16441         */
16442         'beforeselect' : true,
16443         /**
16444          * @event select
16445          * Fires when a list item is selected
16446         * @param {Roo.bootstrap.ComboBox} combo This combo box
16447         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16448         * @param {Number} index The index of the selected item in the dropdown list
16449         */
16450         'select' : true,
16451         /**
16452          * @event beforequery
16453          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16454          * The event object passed has these properties:
16455         * @param {Roo.bootstrap.ComboBox} combo This combo box
16456         * @param {String} query The query
16457         * @param {Boolean} forceAll true to force "all" query
16458         * @param {Boolean} cancel true to cancel the query
16459         * @param {Object} e The query event object
16460         */
16461         'beforequery': true,
16462          /**
16463          * @event add
16464          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16465         * @param {Roo.bootstrap.ComboBox} combo This combo box
16466         */
16467         'add' : true,
16468         /**
16469          * @event edit
16470          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16471         * @param {Roo.bootstrap.ComboBox} combo This combo box
16472         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16473         */
16474         'edit' : true,
16475         /**
16476          * @event remove
16477          * Fires when the remove value from the combobox array
16478         * @param {Roo.bootstrap.ComboBox} combo This combo box
16479         */
16480         'remove' : true,
16481         /**
16482          * @event afterremove
16483          * Fires when the remove value from the combobox array
16484         * @param {Roo.bootstrap.ComboBox} combo This combo box
16485         */
16486         'afterremove' : true,
16487         /**
16488          * @event specialfilter
16489          * Fires when specialfilter
16490             * @param {Roo.bootstrap.ComboBox} combo This combo box
16491             */
16492         'specialfilter' : true,
16493         /**
16494          * @event tick
16495          * Fires when tick the element
16496             * @param {Roo.bootstrap.ComboBox} combo This combo box
16497             */
16498         'tick' : true,
16499         /**
16500          * @event touchviewdisplay
16501          * Fires when touch view require special display (default is using displayField)
16502             * @param {Roo.bootstrap.ComboBox} combo This combo box
16503             * @param {Object} cfg set html .
16504             */
16505         'touchviewdisplay' : true
16506         
16507     });
16508     
16509     this.item = [];
16510     this.tickItems = [];
16511     
16512     this.selectedIndex = -1;
16513     if(this.mode == 'local'){
16514         if(config.queryDelay === undefined){
16515             this.queryDelay = 10;
16516         }
16517         if(config.minChars === undefined){
16518             this.minChars = 0;
16519         }
16520     }
16521 };
16522
16523 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16524      
16525     /**
16526      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16527      * rendering into an Roo.Editor, defaults to false)
16528      */
16529     /**
16530      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16531      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16532      */
16533     /**
16534      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16535      */
16536     /**
16537      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16538      * the dropdown list (defaults to undefined, with no header element)
16539      */
16540
16541      /**
16542      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16543      */
16544      
16545      /**
16546      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16547      */
16548     listWidth: undefined,
16549     /**
16550      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16551      * mode = 'remote' or 'text' if mode = 'local')
16552      */
16553     displayField: undefined,
16554     
16555     /**
16556      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16557      * mode = 'remote' or 'value' if mode = 'local'). 
16558      * Note: use of a valueField requires the user make a selection
16559      * in order for a value to be mapped.
16560      */
16561     valueField: undefined,
16562     /**
16563      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16564      */
16565     modalTitle : '',
16566     
16567     /**
16568      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16569      * field's data value (defaults to the underlying DOM element's name)
16570      */
16571     hiddenName: undefined,
16572     /**
16573      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16574      */
16575     listClass: '',
16576     /**
16577      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16578      */
16579     selectedClass: 'active',
16580     
16581     /**
16582      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16583      */
16584     shadow:'sides',
16585     /**
16586      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16587      * anchor positions (defaults to 'tl-bl')
16588      */
16589     listAlign: 'tl-bl?',
16590     /**
16591      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16592      */
16593     maxHeight: 300,
16594     /**
16595      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16596      * query specified by the allQuery config option (defaults to 'query')
16597      */
16598     triggerAction: 'query',
16599     /**
16600      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16601      * (defaults to 4, does not apply if editable = false)
16602      */
16603     minChars : 4,
16604     /**
16605      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16606      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16607      */
16608     typeAhead: false,
16609     /**
16610      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16611      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16612      */
16613     queryDelay: 500,
16614     /**
16615      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16616      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16617      */
16618     pageSize: 0,
16619     /**
16620      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16621      * when editable = true (defaults to false)
16622      */
16623     selectOnFocus:false,
16624     /**
16625      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16626      */
16627     queryParam: 'query',
16628     /**
16629      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16630      * when mode = 'remote' (defaults to 'Loading...')
16631      */
16632     loadingText: 'Loading...',
16633     /**
16634      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16635      */
16636     resizable: false,
16637     /**
16638      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16639      */
16640     handleHeight : 8,
16641     /**
16642      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16643      * traditional select (defaults to true)
16644      */
16645     editable: true,
16646     /**
16647      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16648      */
16649     allQuery: '',
16650     /**
16651      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16652      */
16653     mode: 'remote',
16654     /**
16655      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16656      * listWidth has a higher value)
16657      */
16658     minListWidth : 70,
16659     /**
16660      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16661      * allow the user to set arbitrary text into the field (defaults to false)
16662      */
16663     forceSelection:false,
16664     /**
16665      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16666      * if typeAhead = true (defaults to 250)
16667      */
16668     typeAheadDelay : 250,
16669     /**
16670      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16671      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16672      */
16673     valueNotFoundText : undefined,
16674     /**
16675      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16676      */
16677     blockFocus : false,
16678     
16679     /**
16680      * @cfg {Boolean} disableClear Disable showing of clear button.
16681      */
16682     disableClear : false,
16683     /**
16684      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16685      */
16686     alwaysQuery : false,
16687     
16688     /**
16689      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16690      */
16691     multiple : false,
16692     
16693     /**
16694      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16695      */
16696     invalidClass : "has-warning",
16697     
16698     /**
16699      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16700      */
16701     validClass : "has-success",
16702     
16703     /**
16704      * @cfg {Boolean} specialFilter (true|false) special filter default false
16705      */
16706     specialFilter : false,
16707     
16708     /**
16709      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16710      */
16711     mobileTouchView : true,
16712     
16713     /**
16714      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16715      */
16716     useNativeIOS : false,
16717     
16718     /**
16719      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16720      */
16721     mobile_restrict_height : false,
16722     
16723     ios_options : false,
16724     
16725     //private
16726     addicon : false,
16727     editicon: false,
16728     
16729     page: 0,
16730     hasQuery: false,
16731     append: false,
16732     loadNext: false,
16733     autoFocus : true,
16734     tickable : false,
16735     btnPosition : 'right',
16736     triggerList : true,
16737     showToggleBtn : true,
16738     animate : true,
16739     emptyResultText: 'Empty',
16740     triggerText : 'Select',
16741     emptyTitle : '',
16742     width : false,
16743     
16744     // element that contains real text value.. (when hidden is used..)
16745     
16746     getAutoCreate : function()
16747     {   
16748         var cfg = false;
16749         //render
16750         /*
16751          * Render classic select for iso
16752          */
16753         
16754         if(Roo.isIOS && this.useNativeIOS){
16755             cfg = this.getAutoCreateNativeIOS();
16756             return cfg;
16757         }
16758         
16759         /*
16760          * Touch Devices
16761          */
16762         
16763         if(Roo.isTouch && this.mobileTouchView){
16764             cfg = this.getAutoCreateTouchView();
16765             return cfg;;
16766         }
16767         
16768         /*
16769          *  Normal ComboBox
16770          */
16771         if(!this.tickable){
16772             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16773             return cfg;
16774         }
16775         
16776         /*
16777          *  ComboBox with tickable selections
16778          */
16779              
16780         var align = this.labelAlign || this.parentLabelAlign();
16781         
16782         cfg = {
16783             cls : 'form-group roo-combobox-tickable' //input-group
16784         };
16785         
16786         var btn_text_select = '';
16787         var btn_text_done = '';
16788         var btn_text_cancel = '';
16789         
16790         if (this.btn_text_show) {
16791             btn_text_select = 'Select';
16792             btn_text_done = 'Done';
16793             btn_text_cancel = 'Cancel'; 
16794         }
16795         
16796         var buttons = {
16797             tag : 'div',
16798             cls : 'tickable-buttons',
16799             cn : [
16800                 {
16801                     tag : 'button',
16802                     type : 'button',
16803                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16804                     //html : this.triggerText
16805                     html: btn_text_select
16806                 },
16807                 {
16808                     tag : 'button',
16809                     type : 'button',
16810                     name : 'ok',
16811                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16812                     //html : 'Done'
16813                     html: btn_text_done
16814                 },
16815                 {
16816                     tag : 'button',
16817                     type : 'button',
16818                     name : 'cancel',
16819                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16820                     //html : 'Cancel'
16821                     html: btn_text_cancel
16822                 }
16823             ]
16824         };
16825         
16826         if(this.editable){
16827             buttons.cn.unshift({
16828                 tag: 'input',
16829                 cls: 'roo-select2-search-field-input'
16830             });
16831         }
16832         
16833         var _this = this;
16834         
16835         Roo.each(buttons.cn, function(c){
16836             if (_this.size) {
16837                 c.cls += ' btn-' + _this.size;
16838             }
16839
16840             if (_this.disabled) {
16841                 c.disabled = true;
16842             }
16843         });
16844         
16845         var box = {
16846             tag: 'div',
16847             style : 'display: contents',
16848             cn: [
16849                 {
16850                     tag: 'input',
16851                     type : 'hidden',
16852                     cls: 'form-hidden-field'
16853                 },
16854                 {
16855                     tag: 'ul',
16856                     cls: 'roo-select2-choices',
16857                     cn:[
16858                         {
16859                             tag: 'li',
16860                             cls: 'roo-select2-search-field',
16861                             cn: [
16862                                 buttons
16863                             ]
16864                         }
16865                     ]
16866                 }
16867             ]
16868         };
16869         
16870         var combobox = {
16871             cls: 'roo-select2-container input-group roo-select2-container-multi',
16872             cn: [
16873                 
16874                 box
16875 //                {
16876 //                    tag: 'ul',
16877 //                    cls: 'typeahead typeahead-long dropdown-menu',
16878 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16879 //                }
16880             ]
16881         };
16882         
16883         if(this.hasFeedback && !this.allowBlank){
16884             
16885             var feedback = {
16886                 tag: 'span',
16887                 cls: 'glyphicon form-control-feedback'
16888             };
16889
16890             combobox.cn.push(feedback);
16891         }
16892         
16893         
16894         
16895         var indicator = {
16896             tag : 'i',
16897             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16898             tooltip : 'This field is required'
16899         };
16900         if (Roo.bootstrap.version == 4) {
16901             indicator = {
16902                 tag : 'i',
16903                 style : 'display:none'
16904             };
16905         }
16906         if (align ==='left' && this.fieldLabel.length) {
16907             
16908             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16909             
16910             cfg.cn = [
16911                 indicator,
16912                 {
16913                     tag: 'label',
16914                     'for' :  id,
16915                     cls : 'control-label col-form-label',
16916                     html : this.fieldLabel
16917
16918                 },
16919                 {
16920                     cls : "", 
16921                     cn: [
16922                         combobox
16923                     ]
16924                 }
16925
16926             ];
16927             
16928             var labelCfg = cfg.cn[1];
16929             var contentCfg = cfg.cn[2];
16930             
16931
16932             if(this.indicatorpos == 'right'){
16933                 
16934                 cfg.cn = [
16935                     {
16936                         tag: 'label',
16937                         'for' :  id,
16938                         cls : 'control-label col-form-label',
16939                         cn : [
16940                             {
16941                                 tag : 'span',
16942                                 html : this.fieldLabel
16943                             },
16944                             indicator
16945                         ]
16946                     },
16947                     {
16948                         cls : "",
16949                         cn: [
16950                             combobox
16951                         ]
16952                     }
16953
16954                 ];
16955                 
16956                 
16957                 
16958                 labelCfg = cfg.cn[0];
16959                 contentCfg = cfg.cn[1];
16960             
16961             }
16962             
16963             if(this.labelWidth > 12){
16964                 labelCfg.style = "width: " + this.labelWidth + 'px';
16965             }
16966             if(this.width * 1 > 0){
16967                 contentCfg.style = "width: " + this.width + 'px';
16968             }
16969             if(this.labelWidth < 13 && this.labelmd == 0){
16970                 this.labelmd = this.labelWidth;
16971             }
16972             
16973             if(this.labellg > 0){
16974                 labelCfg.cls += ' col-lg-' + this.labellg;
16975                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16976             }
16977             
16978             if(this.labelmd > 0){
16979                 labelCfg.cls += ' col-md-' + this.labelmd;
16980                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16981             }
16982             
16983             if(this.labelsm > 0){
16984                 labelCfg.cls += ' col-sm-' + this.labelsm;
16985                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16986             }
16987             
16988             if(this.labelxs > 0){
16989                 labelCfg.cls += ' col-xs-' + this.labelxs;
16990                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16991             }
16992                 
16993                 
16994         } else if ( this.fieldLabel.length) {
16995 //                Roo.log(" label");
16996                  cfg.cn = [
16997                    indicator,
16998                     {
16999                         tag: 'label',
17000                         //cls : 'input-group-addon',
17001                         html : this.fieldLabel
17002                     },
17003                     combobox
17004                 ];
17005                 
17006                 if(this.indicatorpos == 'right'){
17007                     cfg.cn = [
17008                         {
17009                             tag: 'label',
17010                             //cls : 'input-group-addon',
17011                             html : this.fieldLabel
17012                         },
17013                         indicator,
17014                         combobox
17015                     ];
17016                     
17017                 }
17018
17019         } else {
17020             
17021 //                Roo.log(" no label && no align");
17022                 cfg = combobox
17023                      
17024                 
17025         }
17026          
17027         var settings=this;
17028         ['xs','sm','md','lg'].map(function(size){
17029             if (settings[size]) {
17030                 cfg.cls += ' col-' + size + '-' + settings[size];
17031             }
17032         });
17033         
17034         return cfg;
17035         
17036     },
17037     
17038     _initEventsCalled : false,
17039     
17040     // private
17041     initEvents: function()
17042     {   
17043         if (this._initEventsCalled) { // as we call render... prevent looping...
17044             return;
17045         }
17046         this._initEventsCalled = true;
17047         
17048         if (!this.store) {
17049             throw "can not find store for combo";
17050         }
17051         
17052         this.indicator = this.indicatorEl();
17053         
17054         this.store = Roo.factory(this.store, Roo.data);
17055         this.store.parent = this;
17056         
17057         // if we are building from html. then this element is so complex, that we can not really
17058         // use the rendered HTML.
17059         // so we have to trash and replace the previous code.
17060         if (Roo.XComponent.build_from_html) {
17061             // remove this element....
17062             var e = this.el.dom, k=0;
17063             while (e ) { e = e.previousSibling;  ++k;}
17064
17065             this.el.remove();
17066             
17067             this.el=false;
17068             this.rendered = false;
17069             
17070             this.render(this.parent().getChildContainer(true), k);
17071         }
17072         
17073         if(Roo.isIOS && this.useNativeIOS){
17074             this.initIOSView();
17075             return;
17076         }
17077         
17078         /*
17079          * Touch Devices
17080          */
17081         
17082         if(Roo.isTouch && this.mobileTouchView){
17083             this.initTouchView();
17084             return;
17085         }
17086         
17087         if(this.tickable){
17088             this.initTickableEvents();
17089             return;
17090         }
17091         
17092         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17093         
17094         if(this.hiddenName){
17095             
17096             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17097             
17098             this.hiddenField.dom.value =
17099                 this.hiddenValue !== undefined ? this.hiddenValue :
17100                 this.value !== undefined ? this.value : '';
17101
17102             // prevent input submission
17103             this.el.dom.removeAttribute('name');
17104             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17105              
17106              
17107         }
17108         //if(Roo.isGecko){
17109         //    this.el.dom.setAttribute('autocomplete', 'off');
17110         //}
17111         
17112         var cls = 'x-combo-list';
17113         
17114         //this.list = new Roo.Layer({
17115         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17116         //});
17117         
17118         var _this = this;
17119         
17120         (function(){
17121             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17122             _this.list.setWidth(lw);
17123         }).defer(100);
17124         
17125         this.list.on('mouseover', this.onViewOver, this);
17126         this.list.on('mousemove', this.onViewMove, this);
17127         this.list.on('scroll', this.onViewScroll, this);
17128         
17129         /*
17130         this.list.swallowEvent('mousewheel');
17131         this.assetHeight = 0;
17132
17133         if(this.title){
17134             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17135             this.assetHeight += this.header.getHeight();
17136         }
17137
17138         this.innerList = this.list.createChild({cls:cls+'-inner'});
17139         this.innerList.on('mouseover', this.onViewOver, this);
17140         this.innerList.on('mousemove', this.onViewMove, this);
17141         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17142         
17143         if(this.allowBlank && !this.pageSize && !this.disableClear){
17144             this.footer = this.list.createChild({cls:cls+'-ft'});
17145             this.pageTb = new Roo.Toolbar(this.footer);
17146            
17147         }
17148         if(this.pageSize){
17149             this.footer = this.list.createChild({cls:cls+'-ft'});
17150             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17151                     {pageSize: this.pageSize});
17152             
17153         }
17154         
17155         if (this.pageTb && this.allowBlank && !this.disableClear) {
17156             var _this = this;
17157             this.pageTb.add(new Roo.Toolbar.Fill(), {
17158                 cls: 'x-btn-icon x-btn-clear',
17159                 text: '&#160;',
17160                 handler: function()
17161                 {
17162                     _this.collapse();
17163                     _this.clearValue();
17164                     _this.onSelect(false, -1);
17165                 }
17166             });
17167         }
17168         if (this.footer) {
17169             this.assetHeight += this.footer.getHeight();
17170         }
17171         */
17172             
17173         if(!this.tpl){
17174             this.tpl = Roo.bootstrap.version == 4 ?
17175                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17176                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17177         }
17178
17179         this.view = new Roo.View(this.list, this.tpl, {
17180             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17181         });
17182         //this.view.wrapEl.setDisplayed(false);
17183         this.view.on('click', this.onViewClick, this);
17184         
17185         
17186         this.store.on('beforeload', this.onBeforeLoad, this);
17187         this.store.on('load', this.onLoad, this);
17188         this.store.on('loadexception', this.onLoadException, this);
17189         /*
17190         if(this.resizable){
17191             this.resizer = new Roo.Resizable(this.list,  {
17192                pinned:true, handles:'se'
17193             });
17194             this.resizer.on('resize', function(r, w, h){
17195                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17196                 this.listWidth = w;
17197                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17198                 this.restrictHeight();
17199             }, this);
17200             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17201         }
17202         */
17203         if(!this.editable){
17204             this.editable = true;
17205             this.setEditable(false);
17206         }
17207         
17208         /*
17209         
17210         if (typeof(this.events.add.listeners) != 'undefined') {
17211             
17212             this.addicon = this.wrap.createChild(
17213                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17214        
17215             this.addicon.on('click', function(e) {
17216                 this.fireEvent('add', this);
17217             }, this);
17218         }
17219         if (typeof(this.events.edit.listeners) != 'undefined') {
17220             
17221             this.editicon = this.wrap.createChild(
17222                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17223             if (this.addicon) {
17224                 this.editicon.setStyle('margin-left', '40px');
17225             }
17226             this.editicon.on('click', function(e) {
17227                 
17228                 // we fire even  if inothing is selected..
17229                 this.fireEvent('edit', this, this.lastData );
17230                 
17231             }, this);
17232         }
17233         */
17234         
17235         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17236             "up" : function(e){
17237                 this.inKeyMode = true;
17238                 this.selectPrev();
17239             },
17240
17241             "down" : function(e){
17242                 if(!this.isExpanded()){
17243                     this.onTriggerClick();
17244                 }else{
17245                     this.inKeyMode = true;
17246                     this.selectNext();
17247                 }
17248             },
17249
17250             "enter" : function(e){
17251 //                this.onViewClick();
17252                 //return true;
17253                 this.collapse();
17254                 
17255                 if(this.fireEvent("specialkey", this, e)){
17256                     this.onViewClick(false);
17257                 }
17258                 
17259                 return true;
17260             },
17261
17262             "esc" : function(e){
17263                 this.collapse();
17264             },
17265
17266             "tab" : function(e){
17267                 this.collapse();
17268                 
17269                 if(this.fireEvent("specialkey", this, e)){
17270                     this.onViewClick(false);
17271                 }
17272                 
17273                 return true;
17274             },
17275
17276             scope : this,
17277
17278             doRelay : function(foo, bar, hname){
17279                 if(hname == 'down' || this.scope.isExpanded()){
17280                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17281                 }
17282                 return true;
17283             },
17284
17285             forceKeyDown: true
17286         });
17287         
17288         
17289         this.queryDelay = Math.max(this.queryDelay || 10,
17290                 this.mode == 'local' ? 10 : 250);
17291         
17292         
17293         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17294         
17295         if(this.typeAhead){
17296             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17297         }
17298         if(this.editable !== false){
17299             this.inputEl().on("keyup", this.onKeyUp, this);
17300         }
17301         if(this.forceSelection){
17302             this.inputEl().on('blur', this.doForce, this);
17303         }
17304         
17305         if(this.multiple){
17306             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17307             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17308         }
17309     },
17310     
17311     initTickableEvents: function()
17312     {   
17313         this.createList();
17314         
17315         if(this.hiddenName){
17316             
17317             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17318             
17319             this.hiddenField.dom.value =
17320                 this.hiddenValue !== undefined ? this.hiddenValue :
17321                 this.value !== undefined ? this.value : '';
17322
17323             // prevent input submission
17324             this.el.dom.removeAttribute('name');
17325             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17326              
17327              
17328         }
17329         
17330 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17331         
17332         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17333         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17334         if(this.triggerList){
17335             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17336         }
17337          
17338         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17339         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17340         
17341         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17342         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17343         
17344         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17345         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17346         
17347         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17348         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17349         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17350         
17351         this.okBtn.hide();
17352         this.cancelBtn.hide();
17353         
17354         var _this = this;
17355         
17356         (function(){
17357             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17358             _this.list.setWidth(lw);
17359         }).defer(100);
17360         
17361         this.list.on('mouseover', this.onViewOver, this);
17362         this.list.on('mousemove', this.onViewMove, this);
17363         
17364         this.list.on('scroll', this.onViewScroll, this);
17365         
17366         if(!this.tpl){
17367             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17368                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17369         }
17370
17371         this.view = new Roo.View(this.list, this.tpl, {
17372             singleSelect:true,
17373             tickable:true,
17374             parent:this,
17375             store: this.store,
17376             selectedClass: this.selectedClass
17377         });
17378         
17379         //this.view.wrapEl.setDisplayed(false);
17380         this.view.on('click', this.onViewClick, this);
17381         
17382         
17383         
17384         this.store.on('beforeload', this.onBeforeLoad, this);
17385         this.store.on('load', this.onLoad, this);
17386         this.store.on('loadexception', this.onLoadException, this);
17387         
17388         if(this.editable){
17389             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17390                 "up" : function(e){
17391                     this.inKeyMode = true;
17392                     this.selectPrev();
17393                 },
17394
17395                 "down" : function(e){
17396                     this.inKeyMode = true;
17397                     this.selectNext();
17398                 },
17399
17400                 "enter" : function(e){
17401                     if(this.fireEvent("specialkey", this, e)){
17402                         this.onViewClick(false);
17403                     }
17404                     
17405                     return true;
17406                 },
17407
17408                 "esc" : function(e){
17409                     this.onTickableFooterButtonClick(e, false, false);
17410                 },
17411
17412                 "tab" : function(e){
17413                     this.fireEvent("specialkey", this, e);
17414                     
17415                     this.onTickableFooterButtonClick(e, false, false);
17416                     
17417                     return true;
17418                 },
17419
17420                 scope : this,
17421
17422                 doRelay : function(e, fn, key){
17423                     if(this.scope.isExpanded()){
17424                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17425                     }
17426                     return true;
17427                 },
17428
17429                 forceKeyDown: true
17430             });
17431         }
17432         
17433         this.queryDelay = Math.max(this.queryDelay || 10,
17434                 this.mode == 'local' ? 10 : 250);
17435         
17436         
17437         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17438         
17439         if(this.typeAhead){
17440             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17441         }
17442         
17443         if(this.editable !== false){
17444             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17445         }
17446         
17447         this.indicator = this.indicatorEl();
17448         
17449         if(this.indicator){
17450             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17451             this.indicator.hide();
17452         }
17453         
17454     },
17455
17456     onDestroy : function(){
17457         if(this.view){
17458             this.view.setStore(null);
17459             this.view.el.removeAllListeners();
17460             this.view.el.remove();
17461             this.view.purgeListeners();
17462         }
17463         if(this.list){
17464             this.list.dom.innerHTML  = '';
17465         }
17466         
17467         if(this.store){
17468             this.store.un('beforeload', this.onBeforeLoad, this);
17469             this.store.un('load', this.onLoad, this);
17470             this.store.un('loadexception', this.onLoadException, this);
17471         }
17472         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17473     },
17474
17475     // private
17476     fireKey : function(e){
17477         if(e.isNavKeyPress() && !this.list.isVisible()){
17478             this.fireEvent("specialkey", this, e);
17479         }
17480     },
17481
17482     // private
17483     onResize: function(w, h)
17484     {
17485         
17486         
17487 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17488 //        
17489 //        if(typeof w != 'number'){
17490 //            // we do not handle it!?!?
17491 //            return;
17492 //        }
17493 //        var tw = this.trigger.getWidth();
17494 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17495 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17496 //        var x = w - tw;
17497 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17498 //            
17499 //        //this.trigger.setStyle('left', x+'px');
17500 //        
17501 //        if(this.list && this.listWidth === undefined){
17502 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17503 //            this.list.setWidth(lw);
17504 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17505 //        }
17506         
17507     
17508         
17509     },
17510
17511     /**
17512      * Allow or prevent the user from directly editing the field text.  If false is passed,
17513      * the user will only be able to select from the items defined in the dropdown list.  This method
17514      * is the runtime equivalent of setting the 'editable' config option at config time.
17515      * @param {Boolean} value True to allow the user to directly edit the field text
17516      */
17517     setEditable : function(value){
17518         if(value == this.editable){
17519             return;
17520         }
17521         this.editable = value;
17522         if(!value){
17523             this.inputEl().dom.setAttribute('readOnly', true);
17524             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17525             this.inputEl().addClass('x-combo-noedit');
17526         }else{
17527             this.inputEl().dom.removeAttribute('readOnly');
17528             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17529             this.inputEl().removeClass('x-combo-noedit');
17530         }
17531     },
17532
17533     // private
17534     
17535     onBeforeLoad : function(combo,opts){
17536         if(!this.hasFocus){
17537             return;
17538         }
17539          if (!opts.add) {
17540             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17541          }
17542         this.restrictHeight();
17543         this.selectedIndex = -1;
17544     },
17545
17546     // private
17547     onLoad : function(){
17548         
17549         this.hasQuery = false;
17550         
17551         if(!this.hasFocus){
17552             return;
17553         }
17554         
17555         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17556             this.loading.hide();
17557         }
17558         
17559         if(this.store.getCount() > 0){
17560             
17561             this.expand();
17562             this.restrictHeight();
17563             if(this.lastQuery == this.allQuery){
17564                 if(this.editable && !this.tickable){
17565                     this.inputEl().dom.select();
17566                 }
17567                 
17568                 if(
17569                     !this.selectByValue(this.value, true) &&
17570                     this.autoFocus && 
17571                     (
17572                         !this.store.lastOptions ||
17573                         typeof(this.store.lastOptions.add) == 'undefined' || 
17574                         this.store.lastOptions.add != true
17575                     )
17576                 ){
17577                     this.select(0, true);
17578                 }
17579             }else{
17580                 if(this.autoFocus){
17581                     this.selectNext();
17582                 }
17583                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17584                     this.taTask.delay(this.typeAheadDelay);
17585                 }
17586             }
17587         }else{
17588             this.onEmptyResults();
17589         }
17590         
17591         //this.el.focus();
17592     },
17593     // private
17594     onLoadException : function()
17595     {
17596         this.hasQuery = false;
17597         
17598         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17599             this.loading.hide();
17600         }
17601         
17602         if(this.tickable && this.editable){
17603             return;
17604         }
17605         
17606         this.collapse();
17607         // only causes errors at present
17608         //Roo.log(this.store.reader.jsonData);
17609         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17610             // fixme
17611             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17612         //}
17613         
17614         
17615     },
17616     // private
17617     onTypeAhead : function(){
17618         if(this.store.getCount() > 0){
17619             var r = this.store.getAt(0);
17620             var newValue = r.data[this.displayField];
17621             var len = newValue.length;
17622             var selStart = this.getRawValue().length;
17623             
17624             if(selStart != len){
17625                 this.setRawValue(newValue);
17626                 this.selectText(selStart, newValue.length);
17627             }
17628         }
17629     },
17630
17631     // private
17632     onSelect : function(record, index){
17633         
17634         if(this.fireEvent('beforeselect', this, record, index) !== false){
17635         
17636             this.setFromData(index > -1 ? record.data : false);
17637             
17638             this.collapse();
17639             this.fireEvent('select', this, record, index);
17640         }
17641     },
17642
17643     /**
17644      * Returns the currently selected field value or empty string if no value is set.
17645      * @return {String} value The selected value
17646      */
17647     getValue : function()
17648     {
17649         if(Roo.isIOS && this.useNativeIOS){
17650             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17651         }
17652         
17653         if(this.multiple){
17654             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17655         }
17656         
17657         if(this.valueField){
17658             return typeof this.value != 'undefined' ? this.value : '';
17659         }else{
17660             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17661         }
17662     },
17663     
17664     getRawValue : function()
17665     {
17666         if(Roo.isIOS && this.useNativeIOS){
17667             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17668         }
17669         
17670         var v = this.inputEl().getValue();
17671         
17672         return v;
17673     },
17674
17675     /**
17676      * Clears any text/value currently set in the field
17677      */
17678     clearValue : function(){
17679         
17680         if(this.hiddenField){
17681             this.hiddenField.dom.value = '';
17682         }
17683         this.value = '';
17684         this.setRawValue('');
17685         this.lastSelectionText = '';
17686         this.lastData = false;
17687         
17688         var close = this.closeTriggerEl();
17689         
17690         if(close){
17691             close.hide();
17692         }
17693         
17694         this.validate();
17695         
17696     },
17697
17698     /**
17699      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17700      * will be displayed in the field.  If the value does not match the data value of an existing item,
17701      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17702      * Otherwise the field will be blank (although the value will still be set).
17703      * @param {String} value The value to match
17704      */
17705     setValue : function(v)
17706     {
17707         if(Roo.isIOS && this.useNativeIOS){
17708             this.setIOSValue(v);
17709             return;
17710         }
17711         
17712         if(this.multiple){
17713             this.syncValue();
17714             return;
17715         }
17716         
17717         var text = v;
17718         if(this.valueField){
17719             var r = this.findRecord(this.valueField, v);
17720             if(r){
17721                 text = r.data[this.displayField];
17722             }else if(this.valueNotFoundText !== undefined){
17723                 text = this.valueNotFoundText;
17724             }
17725         }
17726         this.lastSelectionText = text;
17727         if(this.hiddenField){
17728             this.hiddenField.dom.value = v;
17729         }
17730         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17731         this.value = v;
17732         
17733         var close = this.closeTriggerEl();
17734         
17735         if(close){
17736             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17737         }
17738         
17739         this.validate();
17740     },
17741     /**
17742      * @property {Object} the last set data for the element
17743      */
17744     
17745     lastData : false,
17746     /**
17747      * Sets the value of the field based on a object which is related to the record format for the store.
17748      * @param {Object} value the value to set as. or false on reset?
17749      */
17750     setFromData : function(o){
17751         
17752         if(this.multiple){
17753             this.addItem(o);
17754             return;
17755         }
17756             
17757         var dv = ''; // display value
17758         var vv = ''; // value value..
17759         this.lastData = o;
17760         if (this.displayField) {
17761             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17762         } else {
17763             // this is an error condition!!!
17764             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17765         }
17766         
17767         if(this.valueField){
17768             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17769         }
17770         
17771         var close = this.closeTriggerEl();
17772         
17773         if(close){
17774             if(dv.length || vv * 1 > 0){
17775                 close.show() ;
17776                 this.blockFocus=true;
17777             } else {
17778                 close.hide();
17779             }             
17780         }
17781         
17782         if(this.hiddenField){
17783             this.hiddenField.dom.value = vv;
17784             
17785             this.lastSelectionText = dv;
17786             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17787             this.value = vv;
17788             return;
17789         }
17790         // no hidden field.. - we store the value in 'value', but still display
17791         // display field!!!!
17792         this.lastSelectionText = dv;
17793         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17794         this.value = vv;
17795         
17796         
17797         
17798     },
17799     // private
17800     reset : function(){
17801         // overridden so that last data is reset..
17802         
17803         if(this.multiple){
17804             this.clearItem();
17805             return;
17806         }
17807         
17808         this.setValue(this.originalValue);
17809         //this.clearInvalid();
17810         this.lastData = false;
17811         if (this.view) {
17812             this.view.clearSelections();
17813         }
17814         
17815         this.validate();
17816     },
17817     // private
17818     findRecord : function(prop, value){
17819         var record;
17820         if(this.store.getCount() > 0){
17821             this.store.each(function(r){
17822                 if(r.data[prop] == value){
17823                     record = r;
17824                     return false;
17825                 }
17826                 return true;
17827             });
17828         }
17829         return record;
17830     },
17831     
17832     getName: function()
17833     {
17834         // returns hidden if it's set..
17835         if (!this.rendered) {return ''};
17836         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17837         
17838     },
17839     // private
17840     onViewMove : function(e, t){
17841         this.inKeyMode = false;
17842     },
17843
17844     // private
17845     onViewOver : function(e, t){
17846         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17847             return;
17848         }
17849         var item = this.view.findItemFromChild(t);
17850         
17851         if(item){
17852             var index = this.view.indexOf(item);
17853             this.select(index, false);
17854         }
17855     },
17856
17857     // private
17858     onViewClick : function(view, doFocus, el, e)
17859     {
17860         var index = this.view.getSelectedIndexes()[0];
17861         
17862         var r = this.store.getAt(index);
17863         
17864         if(this.tickable){
17865             
17866             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17867                 return;
17868             }
17869             
17870             var rm = false;
17871             var _this = this;
17872             
17873             Roo.each(this.tickItems, function(v,k){
17874                 
17875                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17876                     Roo.log(v);
17877                     _this.tickItems.splice(k, 1);
17878                     
17879                     if(typeof(e) == 'undefined' && view == false){
17880                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17881                     }
17882                     
17883                     rm = true;
17884                     return;
17885                 }
17886             });
17887             
17888             if(rm){
17889                 return;
17890             }
17891             
17892             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17893                 this.tickItems.push(r.data);
17894             }
17895             
17896             if(typeof(e) == 'undefined' && view == false){
17897                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17898             }
17899                     
17900             return;
17901         }
17902         
17903         if(r){
17904             this.onSelect(r, index);
17905         }
17906         if(doFocus !== false && !this.blockFocus){
17907             this.inputEl().focus();
17908         }
17909     },
17910
17911     // private
17912     restrictHeight : function(){
17913         //this.innerList.dom.style.height = '';
17914         //var inner = this.innerList.dom;
17915         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17916         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17917         //this.list.beginUpdate();
17918         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17919         this.list.alignTo(this.inputEl(), this.listAlign);
17920         this.list.alignTo(this.inputEl(), this.listAlign);
17921         //this.list.endUpdate();
17922     },
17923
17924     // private
17925     onEmptyResults : function(){
17926         
17927         if(this.tickable && this.editable){
17928             this.hasFocus = false;
17929             this.restrictHeight();
17930             return;
17931         }
17932         
17933         this.collapse();
17934     },
17935
17936     /**
17937      * Returns true if the dropdown list is expanded, else false.
17938      */
17939     isExpanded : function(){
17940         return this.list.isVisible();
17941     },
17942
17943     /**
17944      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17945      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17946      * @param {String} value The data value of the item to select
17947      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17948      * selected item if it is not currently in view (defaults to true)
17949      * @return {Boolean} True if the value matched an item in the list, else false
17950      */
17951     selectByValue : function(v, scrollIntoView){
17952         if(v !== undefined && v !== null){
17953             var r = this.findRecord(this.valueField || this.displayField, v);
17954             if(r){
17955                 this.select(this.store.indexOf(r), scrollIntoView);
17956                 return true;
17957             }
17958         }
17959         return false;
17960     },
17961
17962     /**
17963      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17964      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17965      * @param {Number} index The zero-based index of the list item to select
17966      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17967      * selected item if it is not currently in view (defaults to true)
17968      */
17969     select : function(index, scrollIntoView){
17970         this.selectedIndex = index;
17971         this.view.select(index);
17972         if(scrollIntoView !== false){
17973             var el = this.view.getNode(index);
17974             /*
17975              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17976              */
17977             if(el){
17978                 this.list.scrollChildIntoView(el, false);
17979             }
17980         }
17981     },
17982
17983     // private
17984     selectNext : function(){
17985         var ct = this.store.getCount();
17986         if(ct > 0){
17987             if(this.selectedIndex == -1){
17988                 this.select(0);
17989             }else if(this.selectedIndex < ct-1){
17990                 this.select(this.selectedIndex+1);
17991             }
17992         }
17993     },
17994
17995     // private
17996     selectPrev : function(){
17997         var ct = this.store.getCount();
17998         if(ct > 0){
17999             if(this.selectedIndex == -1){
18000                 this.select(0);
18001             }else if(this.selectedIndex != 0){
18002                 this.select(this.selectedIndex-1);
18003             }
18004         }
18005     },
18006
18007     // private
18008     onKeyUp : function(e){
18009         if(this.editable !== false && !e.isSpecialKey()){
18010             this.lastKey = e.getKey();
18011             this.dqTask.delay(this.queryDelay);
18012         }
18013     },
18014
18015     // private
18016     validateBlur : function(){
18017         return !this.list || !this.list.isVisible();   
18018     },
18019
18020     // private
18021     initQuery : function(){
18022         
18023         var v = this.getRawValue();
18024         
18025         if(this.tickable && this.editable){
18026             v = this.tickableInputEl().getValue();
18027         }
18028         
18029         this.doQuery(v);
18030     },
18031
18032     // private
18033     doForce : function(){
18034         if(this.inputEl().dom.value.length > 0){
18035             this.inputEl().dom.value =
18036                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18037              
18038         }
18039     },
18040
18041     /**
18042      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18043      * query allowing the query action to be canceled if needed.
18044      * @param {String} query The SQL query to execute
18045      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18046      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18047      * saved in the current store (defaults to false)
18048      */
18049     doQuery : function(q, forceAll){
18050         
18051         if(q === undefined || q === null){
18052             q = '';
18053         }
18054         var qe = {
18055             query: q,
18056             forceAll: forceAll,
18057             combo: this,
18058             cancel:false
18059         };
18060         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18061             return false;
18062         }
18063         q = qe.query;
18064         
18065         forceAll = qe.forceAll;
18066         if(forceAll === true || (q.length >= this.minChars)){
18067             
18068             this.hasQuery = true;
18069             
18070             if(this.lastQuery != q || this.alwaysQuery){
18071                 this.lastQuery = q;
18072                 if(this.mode == 'local'){
18073                     this.selectedIndex = -1;
18074                     if(forceAll){
18075                         this.store.clearFilter();
18076                     }else{
18077                         
18078                         if(this.specialFilter){
18079                             this.fireEvent('specialfilter', this);
18080                             this.onLoad();
18081                             return;
18082                         }
18083                         
18084                         this.store.filter(this.displayField, q);
18085                     }
18086                     
18087                     this.store.fireEvent("datachanged", this.store);
18088                     
18089                     this.onLoad();
18090                     
18091                     
18092                 }else{
18093                     
18094                     this.store.baseParams[this.queryParam] = q;
18095                     
18096                     var options = {params : this.getParams(q)};
18097                     
18098                     if(this.loadNext){
18099                         options.add = true;
18100                         options.params.start = this.page * this.pageSize;
18101                     }
18102                     
18103                     this.store.load(options);
18104                     
18105                     /*
18106                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18107                      *  we should expand the list on onLoad
18108                      *  so command out it
18109                      */
18110 //                    this.expand();
18111                 }
18112             }else{
18113                 this.selectedIndex = -1;
18114                 this.onLoad();   
18115             }
18116         }
18117         
18118         this.loadNext = false;
18119     },
18120     
18121     // private
18122     getParams : function(q){
18123         var p = {};
18124         //p[this.queryParam] = q;
18125         
18126         if(this.pageSize){
18127             p.start = 0;
18128             p.limit = this.pageSize;
18129         }
18130         return p;
18131     },
18132
18133     /**
18134      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18135      */
18136     collapse : function(){
18137         if(!this.isExpanded()){
18138             return;
18139         }
18140         
18141         this.list.hide();
18142         
18143         this.hasFocus = false;
18144         
18145         if(this.tickable){
18146             this.okBtn.hide();
18147             this.cancelBtn.hide();
18148             this.trigger.show();
18149             
18150             if(this.editable){
18151                 this.tickableInputEl().dom.value = '';
18152                 this.tickableInputEl().blur();
18153             }
18154             
18155         }
18156         
18157         Roo.get(document).un('mousedown', this.collapseIf, this);
18158         Roo.get(document).un('mousewheel', this.collapseIf, this);
18159         if (!this.editable) {
18160             Roo.get(document).un('keydown', this.listKeyPress, this);
18161         }
18162         this.fireEvent('collapse', this);
18163         
18164         this.validate();
18165     },
18166
18167     // private
18168     collapseIf : function(e){
18169         var in_combo  = e.within(this.el);
18170         var in_list =  e.within(this.list);
18171         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18172         
18173         if (in_combo || in_list || is_list) {
18174             //e.stopPropagation();
18175             return;
18176         }
18177         
18178         if(this.tickable){
18179             this.onTickableFooterButtonClick(e, false, false);
18180         }
18181
18182         this.collapse();
18183         
18184     },
18185
18186     /**
18187      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18188      */
18189     expand : function(){
18190        
18191         if(this.isExpanded() || !this.hasFocus){
18192             return;
18193         }
18194         
18195         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18196         this.list.setWidth(lw);
18197         
18198         Roo.log('expand');
18199         
18200         this.list.show();
18201         
18202         this.restrictHeight();
18203         
18204         if(this.tickable){
18205             
18206             this.tickItems = Roo.apply([], this.item);
18207             
18208             this.okBtn.show();
18209             this.cancelBtn.show();
18210             this.trigger.hide();
18211             
18212             if(this.editable){
18213                 this.tickableInputEl().focus();
18214             }
18215             
18216         }
18217         
18218         Roo.get(document).on('mousedown', this.collapseIf, this);
18219         Roo.get(document).on('mousewheel', this.collapseIf, this);
18220         if (!this.editable) {
18221             Roo.get(document).on('keydown', this.listKeyPress, this);
18222         }
18223         
18224         this.fireEvent('expand', this);
18225     },
18226
18227     // private
18228     // Implements the default empty TriggerField.onTriggerClick function
18229     onTriggerClick : function(e)
18230     {
18231         Roo.log('trigger click');
18232         
18233         if(this.disabled || !this.triggerList){
18234             return;
18235         }
18236         
18237         this.page = 0;
18238         this.loadNext = false;
18239         
18240         if(this.isExpanded()){
18241             this.collapse();
18242             if (!this.blockFocus) {
18243                 this.inputEl().focus();
18244             }
18245             
18246         }else {
18247             this.hasFocus = true;
18248             if(this.triggerAction == 'all') {
18249                 this.doQuery(this.allQuery, true);
18250             } else {
18251                 this.doQuery(this.getRawValue());
18252             }
18253             if (!this.blockFocus) {
18254                 this.inputEl().focus();
18255             }
18256         }
18257     },
18258     
18259     onTickableTriggerClick : function(e)
18260     {
18261         if(this.disabled){
18262             return;
18263         }
18264         
18265         this.page = 0;
18266         this.loadNext = false;
18267         this.hasFocus = true;
18268         
18269         if(this.triggerAction == 'all') {
18270             this.doQuery(this.allQuery, true);
18271         } else {
18272             this.doQuery(this.getRawValue());
18273         }
18274     },
18275     
18276     onSearchFieldClick : function(e)
18277     {
18278         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18279             this.onTickableFooterButtonClick(e, false, false);
18280             return;
18281         }
18282         
18283         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18284             return;
18285         }
18286         
18287         this.page = 0;
18288         this.loadNext = false;
18289         this.hasFocus = true;
18290         
18291         if(this.triggerAction == 'all') {
18292             this.doQuery(this.allQuery, true);
18293         } else {
18294             this.doQuery(this.getRawValue());
18295         }
18296     },
18297     
18298     listKeyPress : function(e)
18299     {
18300         //Roo.log('listkeypress');
18301         // scroll to first matching element based on key pres..
18302         if (e.isSpecialKey()) {
18303             return false;
18304         }
18305         var k = String.fromCharCode(e.getKey()).toUpperCase();
18306         //Roo.log(k);
18307         var match  = false;
18308         var csel = this.view.getSelectedNodes();
18309         var cselitem = false;
18310         if (csel.length) {
18311             var ix = this.view.indexOf(csel[0]);
18312             cselitem  = this.store.getAt(ix);
18313             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18314                 cselitem = false;
18315             }
18316             
18317         }
18318         
18319         this.store.each(function(v) { 
18320             if (cselitem) {
18321                 // start at existing selection.
18322                 if (cselitem.id == v.id) {
18323                     cselitem = false;
18324                 }
18325                 return true;
18326             }
18327                 
18328             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18329                 match = this.store.indexOf(v);
18330                 return false;
18331             }
18332             return true;
18333         }, this);
18334         
18335         if (match === false) {
18336             return true; // no more action?
18337         }
18338         // scroll to?
18339         this.view.select(match);
18340         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18341         sn.scrollIntoView(sn.dom.parentNode, false);
18342     },
18343     
18344     onViewScroll : function(e, t){
18345         
18346         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){
18347             return;
18348         }
18349         
18350         this.hasQuery = true;
18351         
18352         this.loading = this.list.select('.loading', true).first();
18353         
18354         if(this.loading === null){
18355             this.list.createChild({
18356                 tag: 'div',
18357                 cls: 'loading roo-select2-more-results roo-select2-active',
18358                 html: 'Loading more results...'
18359             });
18360             
18361             this.loading = this.list.select('.loading', true).first();
18362             
18363             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18364             
18365             this.loading.hide();
18366         }
18367         
18368         this.loading.show();
18369         
18370         var _combo = this;
18371         
18372         this.page++;
18373         this.loadNext = true;
18374         
18375         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18376         
18377         return;
18378     },
18379     
18380     addItem : function(o)
18381     {   
18382         var dv = ''; // display value
18383         
18384         if (this.displayField) {
18385             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18386         } else {
18387             // this is an error condition!!!
18388             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18389         }
18390         
18391         if(!dv.length){
18392             return;
18393         }
18394         
18395         var choice = this.choices.createChild({
18396             tag: 'li',
18397             cls: 'roo-select2-search-choice',
18398             cn: [
18399                 {
18400                     tag: 'div',
18401                     html: dv
18402                 },
18403                 {
18404                     tag: 'a',
18405                     href: '#',
18406                     cls: 'roo-select2-search-choice-close fa fa-times',
18407                     tabindex: '-1'
18408                 }
18409             ]
18410             
18411         }, this.searchField);
18412         
18413         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18414         
18415         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18416         
18417         this.item.push(o);
18418         
18419         this.lastData = o;
18420         
18421         this.syncValue();
18422         
18423         this.inputEl().dom.value = '';
18424         
18425         this.validate();
18426     },
18427     
18428     onRemoveItem : function(e, _self, o)
18429     {
18430         e.preventDefault();
18431         
18432         this.lastItem = Roo.apply([], this.item);
18433         
18434         var index = this.item.indexOf(o.data) * 1;
18435         
18436         if( index < 0){
18437             Roo.log('not this item?!');
18438             return;
18439         }
18440         
18441         this.item.splice(index, 1);
18442         o.item.remove();
18443         
18444         this.syncValue();
18445         
18446         this.fireEvent('remove', this, e);
18447         
18448         this.validate();
18449         
18450     },
18451     
18452     syncValue : function()
18453     {
18454         if(!this.item.length){
18455             this.clearValue();
18456             return;
18457         }
18458             
18459         var value = [];
18460         var _this = this;
18461         Roo.each(this.item, function(i){
18462             if(_this.valueField){
18463                 value.push(i[_this.valueField]);
18464                 return;
18465             }
18466
18467             value.push(i);
18468         });
18469
18470         this.value = value.join(',');
18471
18472         if(this.hiddenField){
18473             this.hiddenField.dom.value = this.value;
18474         }
18475         
18476         this.store.fireEvent("datachanged", this.store);
18477         
18478         this.validate();
18479     },
18480     
18481     clearItem : function()
18482     {
18483         if(!this.multiple){
18484             return;
18485         }
18486         
18487         this.item = [];
18488         
18489         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18490            c.remove();
18491         });
18492         
18493         this.syncValue();
18494         
18495         this.validate();
18496         
18497         if(this.tickable && !Roo.isTouch){
18498             this.view.refresh();
18499         }
18500     },
18501     
18502     inputEl: function ()
18503     {
18504         if(Roo.isIOS && this.useNativeIOS){
18505             return this.el.select('select.roo-ios-select', true).first();
18506         }
18507         
18508         if(Roo.isTouch && this.mobileTouchView){
18509             return this.el.select('input.form-control',true).first();
18510         }
18511         
18512         if(this.tickable){
18513             return this.searchField;
18514         }
18515         
18516         return this.el.select('input.form-control',true).first();
18517     },
18518     
18519     onTickableFooterButtonClick : function(e, btn, el)
18520     {
18521         e.preventDefault();
18522         
18523         this.lastItem = Roo.apply([], this.item);
18524         
18525         if(btn && btn.name == 'cancel'){
18526             this.tickItems = Roo.apply([], this.item);
18527             this.collapse();
18528             return;
18529         }
18530         
18531         this.clearItem();
18532         
18533         var _this = this;
18534         
18535         Roo.each(this.tickItems, function(o){
18536             _this.addItem(o);
18537         });
18538         
18539         this.collapse();
18540         
18541     },
18542     
18543     validate : function()
18544     {
18545         if(this.getVisibilityEl().hasClass('hidden')){
18546             return true;
18547         }
18548         
18549         var v = this.getRawValue();
18550         
18551         if(this.multiple){
18552             v = this.getValue();
18553         }
18554         
18555         if(this.disabled || this.allowBlank || v.length){
18556             this.markValid();
18557             return true;
18558         }
18559         
18560         this.markInvalid();
18561         return false;
18562     },
18563     
18564     tickableInputEl : function()
18565     {
18566         if(!this.tickable || !this.editable){
18567             return this.inputEl();
18568         }
18569         
18570         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18571     },
18572     
18573     
18574     getAutoCreateTouchView : function()
18575     {
18576         var id = Roo.id();
18577         
18578         var cfg = {
18579             cls: 'form-group' //input-group
18580         };
18581         
18582         var input =  {
18583             tag: 'input',
18584             id : id,
18585             type : this.inputType,
18586             cls : 'form-control x-combo-noedit',
18587             autocomplete: 'new-password',
18588             placeholder : this.placeholder || '',
18589             readonly : true
18590         };
18591         
18592         if (this.name) {
18593             input.name = this.name;
18594         }
18595         
18596         if (this.size) {
18597             input.cls += ' input-' + this.size;
18598         }
18599         
18600         if (this.disabled) {
18601             input.disabled = true;
18602         }
18603         
18604         var inputblock = {
18605             cls : 'roo-combobox-wrap',
18606             cn : [
18607                 input
18608             ]
18609         };
18610         
18611         if(this.before){
18612             inputblock.cls += ' input-group';
18613             
18614             inputblock.cn.unshift({
18615                 tag :'span',
18616                 cls : 'input-group-addon input-group-prepend input-group-text',
18617                 html : this.before
18618             });
18619         }
18620         
18621         if(this.removable && !this.multiple){
18622             inputblock.cls += ' roo-removable';
18623             
18624             inputblock.cn.push({
18625                 tag: 'button',
18626                 html : 'x',
18627                 cls : 'roo-combo-removable-btn close'
18628             });
18629         }
18630
18631         if(this.hasFeedback && !this.allowBlank){
18632             
18633             inputblock.cls += ' has-feedback';
18634             
18635             inputblock.cn.push({
18636                 tag: 'span',
18637                 cls: 'glyphicon form-control-feedback'
18638             });
18639             
18640         }
18641         
18642         if (this.after) {
18643             
18644             inputblock.cls += (this.before) ? '' : ' input-group';
18645             
18646             inputblock.cn.push({
18647                 tag :'span',
18648                 cls : 'input-group-addon input-group-append input-group-text',
18649                 html : this.after
18650             });
18651         }
18652
18653         
18654         var ibwrap = inputblock;
18655         
18656         if(this.multiple){
18657             ibwrap = {
18658                 tag: 'ul',
18659                 cls: 'roo-select2-choices',
18660                 cn:[
18661                     {
18662                         tag: 'li',
18663                         cls: 'roo-select2-search-field',
18664                         cn: [
18665
18666                             inputblock
18667                         ]
18668                     }
18669                 ]
18670             };
18671         
18672             
18673         }
18674         
18675         var combobox = {
18676             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18677             cn: [
18678                 {
18679                     tag: 'input',
18680                     type : 'hidden',
18681                     cls: 'form-hidden-field'
18682                 },
18683                 ibwrap
18684             ]
18685         };
18686         
18687         if(!this.multiple && this.showToggleBtn){
18688             
18689             var caret = {
18690                 cls: 'caret'
18691             };
18692             
18693             if (this.caret != false) {
18694                 caret = {
18695                      tag: 'i',
18696                      cls: 'fa fa-' + this.caret
18697                 };
18698                 
18699             }
18700             
18701             combobox.cn.push({
18702                 tag :'span',
18703                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18704                 cn : [
18705                     Roo.bootstrap.version == 3 ? caret : '',
18706                     {
18707                         tag: 'span',
18708                         cls: 'combobox-clear',
18709                         cn  : [
18710                             {
18711                                 tag : 'i',
18712                                 cls: 'icon-remove'
18713                             }
18714                         ]
18715                     }
18716                 ]
18717
18718             })
18719         }
18720         
18721         if(this.multiple){
18722             combobox.cls += ' roo-select2-container-multi';
18723         }
18724         
18725         var required =  this.allowBlank ?  {
18726                     tag : 'i',
18727                     style: 'display: none'
18728                 } : {
18729                    tag : 'i',
18730                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18731                    tooltip : 'This field is required'
18732                 };
18733         
18734         var align = this.labelAlign || this.parentLabelAlign();
18735         
18736         if (align ==='left' && this.fieldLabel.length) {
18737
18738             cfg.cn = [
18739                 required,
18740                 {
18741                     tag: 'label',
18742                     cls : 'control-label col-form-label',
18743                     html : this.fieldLabel
18744
18745                 },
18746                 {
18747                     cls : 'roo-combobox-wrap ', 
18748                     cn: [
18749                         combobox
18750                     ]
18751                 }
18752             ];
18753             
18754             var labelCfg = cfg.cn[1];
18755             var contentCfg = cfg.cn[2];
18756             
18757
18758             if(this.indicatorpos == 'right'){
18759                 cfg.cn = [
18760                     {
18761                         tag: 'label',
18762                         'for' :  id,
18763                         cls : 'control-label col-form-label',
18764                         cn : [
18765                             {
18766                                 tag : 'span',
18767                                 html : this.fieldLabel
18768                             },
18769                             required
18770                         ]
18771                     },
18772                     {
18773                         cls : "roo-combobox-wrap ",
18774                         cn: [
18775                             combobox
18776                         ]
18777                     }
18778
18779                 ];
18780                 
18781                 labelCfg = cfg.cn[0];
18782                 contentCfg = cfg.cn[1];
18783             }
18784             
18785            
18786             
18787             if(this.labelWidth > 12){
18788                 labelCfg.style = "width: " + this.labelWidth + 'px';
18789             }
18790            
18791             if(this.labelWidth < 13 && this.labelmd == 0){
18792                 this.labelmd = this.labelWidth;
18793             }
18794             
18795             if(this.labellg > 0){
18796                 labelCfg.cls += ' col-lg-' + this.labellg;
18797                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18798             }
18799             
18800             if(this.labelmd > 0){
18801                 labelCfg.cls += ' col-md-' + this.labelmd;
18802                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18803             }
18804             
18805             if(this.labelsm > 0){
18806                 labelCfg.cls += ' col-sm-' + this.labelsm;
18807                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18808             }
18809             
18810             if(this.labelxs > 0){
18811                 labelCfg.cls += ' col-xs-' + this.labelxs;
18812                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18813             }
18814                 
18815                 
18816         } else if ( this.fieldLabel.length) {
18817             cfg.cn = [
18818                required,
18819                 {
18820                     tag: 'label',
18821                     cls : 'control-label',
18822                     html : this.fieldLabel
18823
18824                 },
18825                 {
18826                     cls : '', 
18827                     cn: [
18828                         combobox
18829                     ]
18830                 }
18831             ];
18832             
18833             if(this.indicatorpos == 'right'){
18834                 cfg.cn = [
18835                     {
18836                         tag: 'label',
18837                         cls : 'control-label',
18838                         html : this.fieldLabel,
18839                         cn : [
18840                             required
18841                         ]
18842                     },
18843                     {
18844                         cls : '', 
18845                         cn: [
18846                             combobox
18847                         ]
18848                     }
18849                 ];
18850             }
18851         } else {
18852             cfg.cn = combobox;    
18853         }
18854         
18855         
18856         var settings = this;
18857         
18858         ['xs','sm','md','lg'].map(function(size){
18859             if (settings[size]) {
18860                 cfg.cls += ' col-' + size + '-' + settings[size];
18861             }
18862         });
18863         
18864         return cfg;
18865     },
18866     
18867     initTouchView : function()
18868     {
18869         this.renderTouchView();
18870         
18871         this.touchViewEl.on('scroll', function(){
18872             this.el.dom.scrollTop = 0;
18873         }, this);
18874         
18875         this.originalValue = this.getValue();
18876         
18877         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18878         
18879         this.inputEl().on("click", this.showTouchView, this);
18880         if (this.triggerEl) {
18881             this.triggerEl.on("click", this.showTouchView, this);
18882         }
18883         
18884         
18885         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18886         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18887         
18888         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18889         
18890         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18891         this.store.on('load', this.onTouchViewLoad, this);
18892         this.store.on('loadexception', this.onTouchViewLoadException, this);
18893         
18894         if(this.hiddenName){
18895             
18896             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18897             
18898             this.hiddenField.dom.value =
18899                 this.hiddenValue !== undefined ? this.hiddenValue :
18900                 this.value !== undefined ? this.value : '';
18901         
18902             this.el.dom.removeAttribute('name');
18903             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18904         }
18905         
18906         if(this.multiple){
18907             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18908             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18909         }
18910         
18911         if(this.removable && !this.multiple){
18912             var close = this.closeTriggerEl();
18913             if(close){
18914                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18915                 close.on('click', this.removeBtnClick, this, close);
18916             }
18917         }
18918         /*
18919          * fix the bug in Safari iOS8
18920          */
18921         this.inputEl().on("focus", function(e){
18922             document.activeElement.blur();
18923         }, this);
18924         
18925         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18926         
18927         return;
18928         
18929         
18930     },
18931     
18932     renderTouchView : function()
18933     {
18934         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18935         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18936         
18937         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18938         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18939         
18940         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18941         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18942         this.touchViewBodyEl.setStyle('overflow', 'auto');
18943         
18944         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18945         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18946         
18947         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18948         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18949         
18950     },
18951     
18952     showTouchView : function()
18953     {
18954         if(this.disabled){
18955             return;
18956         }
18957         
18958         this.touchViewHeaderEl.hide();
18959
18960         if(this.modalTitle.length){
18961             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18962             this.touchViewHeaderEl.show();
18963         }
18964
18965         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18966         this.touchViewEl.show();
18967
18968         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18969         
18970         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18971         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18972
18973         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18974
18975         if(this.modalTitle.length){
18976             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18977         }
18978         
18979         this.touchViewBodyEl.setHeight(bodyHeight);
18980
18981         if(this.animate){
18982             var _this = this;
18983             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18984         }else{
18985             this.touchViewEl.addClass(['in','show']);
18986         }
18987         
18988         if(this._touchViewMask){
18989             Roo.get(document.body).addClass("x-body-masked");
18990             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18991             this._touchViewMask.setStyle('z-index', 10000);
18992             this._touchViewMask.addClass('show');
18993         }
18994         
18995         this.doTouchViewQuery();
18996         
18997     },
18998     
18999     hideTouchView : function()
19000     {
19001         this.touchViewEl.removeClass(['in','show']);
19002
19003         if(this.animate){
19004             var _this = this;
19005             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19006         }else{
19007             this.touchViewEl.setStyle('display', 'none');
19008         }
19009         
19010         if(this._touchViewMask){
19011             this._touchViewMask.removeClass('show');
19012             Roo.get(document.body).removeClass("x-body-masked");
19013         }
19014     },
19015     
19016     setTouchViewValue : function()
19017     {
19018         if(this.multiple){
19019             this.clearItem();
19020         
19021             var _this = this;
19022
19023             Roo.each(this.tickItems, function(o){
19024                 this.addItem(o);
19025             }, this);
19026         }
19027         
19028         this.hideTouchView();
19029     },
19030     
19031     doTouchViewQuery : function()
19032     {
19033         var qe = {
19034             query: '',
19035             forceAll: true,
19036             combo: this,
19037             cancel:false
19038         };
19039         
19040         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19041             return false;
19042         }
19043         
19044         if(!this.alwaysQuery || this.mode == 'local'){
19045             this.onTouchViewLoad();
19046             return;
19047         }
19048         
19049         this.store.load();
19050     },
19051     
19052     onTouchViewBeforeLoad : function(combo,opts)
19053     {
19054         return;
19055     },
19056
19057     // private
19058     onTouchViewLoad : function()
19059     {
19060         if(this.store.getCount() < 1){
19061             this.onTouchViewEmptyResults();
19062             return;
19063         }
19064         
19065         this.clearTouchView();
19066         
19067         var rawValue = this.getRawValue();
19068         
19069         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19070         
19071         this.tickItems = [];
19072         
19073         this.store.data.each(function(d, rowIndex){
19074             var row = this.touchViewListGroup.createChild(template);
19075             
19076             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19077                 row.addClass(d.data.cls);
19078             }
19079             
19080             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19081                 var cfg = {
19082                     data : d.data,
19083                     html : d.data[this.displayField]
19084                 };
19085                 
19086                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19087                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19088                 }
19089             }
19090             row.removeClass('selected');
19091             if(!this.multiple && this.valueField &&
19092                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19093             {
19094                 // radio buttons..
19095                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19096                 row.addClass('selected');
19097             }
19098             
19099             if(this.multiple && this.valueField &&
19100                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19101             {
19102                 
19103                 // checkboxes...
19104                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19105                 this.tickItems.push(d.data);
19106             }
19107             
19108             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19109             
19110         }, this);
19111         
19112         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19113         
19114         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19115
19116         if(this.modalTitle.length){
19117             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19118         }
19119
19120         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19121         
19122         if(this.mobile_restrict_height && listHeight < bodyHeight){
19123             this.touchViewBodyEl.setHeight(listHeight);
19124         }
19125         
19126         var _this = this;
19127         
19128         if(firstChecked && listHeight > bodyHeight){
19129             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19130         }
19131         
19132     },
19133     
19134     onTouchViewLoadException : function()
19135     {
19136         this.hideTouchView();
19137     },
19138     
19139     onTouchViewEmptyResults : function()
19140     {
19141         this.clearTouchView();
19142         
19143         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19144         
19145         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19146         
19147     },
19148     
19149     clearTouchView : function()
19150     {
19151         this.touchViewListGroup.dom.innerHTML = '';
19152     },
19153     
19154     onTouchViewClick : function(e, el, o)
19155     {
19156         e.preventDefault();
19157         
19158         var row = o.row;
19159         var rowIndex = o.rowIndex;
19160         
19161         var r = this.store.getAt(rowIndex);
19162         
19163         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19164             
19165             if(!this.multiple){
19166                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19167                     c.dom.removeAttribute('checked');
19168                 }, this);
19169
19170                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19171
19172                 this.setFromData(r.data);
19173
19174                 var close = this.closeTriggerEl();
19175
19176                 if(close){
19177                     close.show();
19178                 }
19179
19180                 this.hideTouchView();
19181
19182                 this.fireEvent('select', this, r, rowIndex);
19183
19184                 return;
19185             }
19186
19187             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19188                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19189                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19190                 return;
19191             }
19192
19193             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19194             this.addItem(r.data);
19195             this.tickItems.push(r.data);
19196         }
19197     },
19198     
19199     getAutoCreateNativeIOS : function()
19200     {
19201         var cfg = {
19202             cls: 'form-group' //input-group,
19203         };
19204         
19205         var combobox =  {
19206             tag: 'select',
19207             cls : 'roo-ios-select'
19208         };
19209         
19210         if (this.name) {
19211             combobox.name = this.name;
19212         }
19213         
19214         if (this.disabled) {
19215             combobox.disabled = true;
19216         }
19217         
19218         var settings = this;
19219         
19220         ['xs','sm','md','lg'].map(function(size){
19221             if (settings[size]) {
19222                 cfg.cls += ' col-' + size + '-' + settings[size];
19223             }
19224         });
19225         
19226         cfg.cn = combobox;
19227         
19228         return cfg;
19229         
19230     },
19231     
19232     initIOSView : function()
19233     {
19234         this.store.on('load', this.onIOSViewLoad, this);
19235         
19236         return;
19237     },
19238     
19239     onIOSViewLoad : function()
19240     {
19241         if(this.store.getCount() < 1){
19242             return;
19243         }
19244         
19245         this.clearIOSView();
19246         
19247         if(this.allowBlank) {
19248             
19249             var default_text = '-- SELECT --';
19250             
19251             if(this.placeholder.length){
19252                 default_text = this.placeholder;
19253             }
19254             
19255             if(this.emptyTitle.length){
19256                 default_text += ' - ' + this.emptyTitle + ' -';
19257             }
19258             
19259             var opt = this.inputEl().createChild({
19260                 tag: 'option',
19261                 value : 0,
19262                 html : default_text
19263             });
19264             
19265             var o = {};
19266             o[this.valueField] = 0;
19267             o[this.displayField] = default_text;
19268             
19269             this.ios_options.push({
19270                 data : o,
19271                 el : opt
19272             });
19273             
19274         }
19275         
19276         this.store.data.each(function(d, rowIndex){
19277             
19278             var html = '';
19279             
19280             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19281                 html = d.data[this.displayField];
19282             }
19283             
19284             var value = '';
19285             
19286             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19287                 value = d.data[this.valueField];
19288             }
19289             
19290             var option = {
19291                 tag: 'option',
19292                 value : value,
19293                 html : html
19294             };
19295             
19296             if(this.value == d.data[this.valueField]){
19297                 option['selected'] = true;
19298             }
19299             
19300             var opt = this.inputEl().createChild(option);
19301             
19302             this.ios_options.push({
19303                 data : d.data,
19304                 el : opt
19305             });
19306             
19307         }, this);
19308         
19309         this.inputEl().on('change', function(){
19310            this.fireEvent('select', this);
19311         }, this);
19312         
19313     },
19314     
19315     clearIOSView: function()
19316     {
19317         this.inputEl().dom.innerHTML = '';
19318         
19319         this.ios_options = [];
19320     },
19321     
19322     setIOSValue: function(v)
19323     {
19324         this.value = v;
19325         
19326         if(!this.ios_options){
19327             return;
19328         }
19329         
19330         Roo.each(this.ios_options, function(opts){
19331            
19332            opts.el.dom.removeAttribute('selected');
19333            
19334            if(opts.data[this.valueField] != v){
19335                return;
19336            }
19337            
19338            opts.el.dom.setAttribute('selected', true);
19339            
19340         }, this);
19341     }
19342
19343     /** 
19344     * @cfg {Boolean} grow 
19345     * @hide 
19346     */
19347     /** 
19348     * @cfg {Number} growMin 
19349     * @hide 
19350     */
19351     /** 
19352     * @cfg {Number} growMax 
19353     * @hide 
19354     */
19355     /**
19356      * @hide
19357      * @method autoSize
19358      */
19359 });
19360
19361 Roo.apply(Roo.bootstrap.ComboBox,  {
19362     
19363     header : {
19364         tag: 'div',
19365         cls: 'modal-header',
19366         cn: [
19367             {
19368                 tag: 'h4',
19369                 cls: 'modal-title'
19370             }
19371         ]
19372     },
19373     
19374     body : {
19375         tag: 'div',
19376         cls: 'modal-body',
19377         cn: [
19378             {
19379                 tag: 'ul',
19380                 cls: 'list-group'
19381             }
19382         ]
19383     },
19384     
19385     listItemRadio : {
19386         tag: 'li',
19387         cls: 'list-group-item',
19388         cn: [
19389             {
19390                 tag: 'span',
19391                 cls: 'roo-combobox-list-group-item-value'
19392             },
19393             {
19394                 tag: 'div',
19395                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19396                 cn: [
19397                     {
19398                         tag: 'input',
19399                         type: 'radio'
19400                     },
19401                     {
19402                         tag: 'label'
19403                     }
19404                 ]
19405             }
19406         ]
19407     },
19408     
19409     listItemCheckbox : {
19410         tag: 'li',
19411         cls: 'list-group-item',
19412         cn: [
19413             {
19414                 tag: 'span',
19415                 cls: 'roo-combobox-list-group-item-value'
19416             },
19417             {
19418                 tag: 'div',
19419                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19420                 cn: [
19421                     {
19422                         tag: 'input',
19423                         type: 'checkbox'
19424                     },
19425                     {
19426                         tag: 'label'
19427                     }
19428                 ]
19429             }
19430         ]
19431     },
19432     
19433     emptyResult : {
19434         tag: 'div',
19435         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19436     },
19437     
19438     footer : {
19439         tag: 'div',
19440         cls: 'modal-footer',
19441         cn: [
19442             {
19443                 tag: 'div',
19444                 cls: 'row',
19445                 cn: [
19446                     {
19447                         tag: 'div',
19448                         cls: 'col-xs-6 text-left',
19449                         cn: {
19450                             tag: 'button',
19451                             cls: 'btn btn-danger roo-touch-view-cancel',
19452                             html: 'Cancel'
19453                         }
19454                     },
19455                     {
19456                         tag: 'div',
19457                         cls: 'col-xs-6 text-right',
19458                         cn: {
19459                             tag: 'button',
19460                             cls: 'btn btn-success roo-touch-view-ok',
19461                             html: 'OK'
19462                         }
19463                     }
19464                 ]
19465             }
19466         ]
19467         
19468     }
19469 });
19470
19471 Roo.apply(Roo.bootstrap.ComboBox,  {
19472     
19473     touchViewTemplate : {
19474         tag: 'div',
19475         cls: 'modal fade roo-combobox-touch-view',
19476         cn: [
19477             {
19478                 tag: 'div',
19479                 cls: 'modal-dialog',
19480                 style : 'position:fixed', // we have to fix position....
19481                 cn: [
19482                     {
19483                         tag: 'div',
19484                         cls: 'modal-content',
19485                         cn: [
19486                             Roo.bootstrap.ComboBox.header,
19487                             Roo.bootstrap.ComboBox.body,
19488                             Roo.bootstrap.ComboBox.footer
19489                         ]
19490                     }
19491                 ]
19492             }
19493         ]
19494     }
19495 });/*
19496  * Based on:
19497  * Ext JS Library 1.1.1
19498  * Copyright(c) 2006-2007, Ext JS, LLC.
19499  *
19500  * Originally Released Under LGPL - original licence link has changed is not relivant.
19501  *
19502  * Fork - LGPL
19503  * <script type="text/javascript">
19504  */
19505
19506 /**
19507  * @class Roo.View
19508  * @extends Roo.util.Observable
19509  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19510  * This class also supports single and multi selection modes. <br>
19511  * Create a data model bound view:
19512  <pre><code>
19513  var store = new Roo.data.Store(...);
19514
19515  var view = new Roo.View({
19516     el : "my-element",
19517     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19518  
19519     singleSelect: true,
19520     selectedClass: "ydataview-selected",
19521     store: store
19522  });
19523
19524  // listen for node click?
19525  view.on("click", function(vw, index, node, e){
19526  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19527  });
19528
19529  // load XML data
19530  dataModel.load("foobar.xml");
19531  </code></pre>
19532  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19533  * <br><br>
19534  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19535  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19536  * 
19537  * Note: old style constructor is still suported (container, template, config)
19538  * 
19539  * @constructor
19540  * Create a new View
19541  * @param {Object} config The config object
19542  * 
19543  */
19544 Roo.View = function(config, depreciated_tpl, depreciated_config){
19545     
19546     this.parent = false;
19547     
19548     if (typeof(depreciated_tpl) == 'undefined') {
19549         // new way.. - universal constructor.
19550         Roo.apply(this, config);
19551         this.el  = Roo.get(this.el);
19552     } else {
19553         // old format..
19554         this.el  = Roo.get(config);
19555         this.tpl = depreciated_tpl;
19556         Roo.apply(this, depreciated_config);
19557     }
19558     this.wrapEl  = this.el.wrap().wrap();
19559     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19560     
19561     
19562     if(typeof(this.tpl) == "string"){
19563         this.tpl = new Roo.Template(this.tpl);
19564     } else {
19565         // support xtype ctors..
19566         this.tpl = new Roo.factory(this.tpl, Roo);
19567     }
19568     
19569     
19570     this.tpl.compile();
19571     
19572     /** @private */
19573     this.addEvents({
19574         /**
19575          * @event beforeclick
19576          * Fires before a click is processed. Returns false to cancel the default action.
19577          * @param {Roo.View} this
19578          * @param {Number} index The index of the target node
19579          * @param {HTMLElement} node The target node
19580          * @param {Roo.EventObject} e The raw event object
19581          */
19582             "beforeclick" : true,
19583         /**
19584          * @event click
19585          * Fires when a template node is clicked.
19586          * @param {Roo.View} this
19587          * @param {Number} index The index of the target node
19588          * @param {HTMLElement} node The target node
19589          * @param {Roo.EventObject} e The raw event object
19590          */
19591             "click" : true,
19592         /**
19593          * @event dblclick
19594          * Fires when a template node is double clicked.
19595          * @param {Roo.View} this
19596          * @param {Number} index The index of the target node
19597          * @param {HTMLElement} node The target node
19598          * @param {Roo.EventObject} e The raw event object
19599          */
19600             "dblclick" : true,
19601         /**
19602          * @event contextmenu
19603          * Fires when a template node is right clicked.
19604          * @param {Roo.View} this
19605          * @param {Number} index The index of the target node
19606          * @param {HTMLElement} node The target node
19607          * @param {Roo.EventObject} e The raw event object
19608          */
19609             "contextmenu" : true,
19610         /**
19611          * @event selectionchange
19612          * Fires when the selected nodes change.
19613          * @param {Roo.View} this
19614          * @param {Array} selections Array of the selected nodes
19615          */
19616             "selectionchange" : true,
19617     
19618         /**
19619          * @event beforeselect
19620          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19621          * @param {Roo.View} this
19622          * @param {HTMLElement} node The node to be selected
19623          * @param {Array} selections Array of currently selected nodes
19624          */
19625             "beforeselect" : true,
19626         /**
19627          * @event preparedata
19628          * Fires on every row to render, to allow you to change the data.
19629          * @param {Roo.View} this
19630          * @param {Object} data to be rendered (change this)
19631          */
19632           "preparedata" : true
19633           
19634           
19635         });
19636
19637
19638
19639     this.el.on({
19640         "click": this.onClick,
19641         "dblclick": this.onDblClick,
19642         "contextmenu": this.onContextMenu,
19643         scope:this
19644     });
19645
19646     this.selections = [];
19647     this.nodes = [];
19648     this.cmp = new Roo.CompositeElementLite([]);
19649     if(this.store){
19650         this.store = Roo.factory(this.store, Roo.data);
19651         this.setStore(this.store, true);
19652     }
19653     
19654     if ( this.footer && this.footer.xtype) {
19655            
19656          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19657         
19658         this.footer.dataSource = this.store;
19659         this.footer.container = fctr;
19660         this.footer = Roo.factory(this.footer, Roo);
19661         fctr.insertFirst(this.el);
19662         
19663         // this is a bit insane - as the paging toolbar seems to detach the el..
19664 //        dom.parentNode.parentNode.parentNode
19665          // they get detached?
19666     }
19667     
19668     
19669     Roo.View.superclass.constructor.call(this);
19670     
19671     
19672 };
19673
19674 Roo.extend(Roo.View, Roo.util.Observable, {
19675     
19676      /**
19677      * @cfg {Roo.data.Store} store Data store to load data from.
19678      */
19679     store : false,
19680     
19681     /**
19682      * @cfg {String|Roo.Element} el The container element.
19683      */
19684     el : '',
19685     
19686     /**
19687      * @cfg {String|Roo.Template} tpl The template used by this View 
19688      */
19689     tpl : false,
19690     /**
19691      * @cfg {String} dataName the named area of the template to use as the data area
19692      *                          Works with domtemplates roo-name="name"
19693      */
19694     dataName: false,
19695     /**
19696      * @cfg {String} selectedClass The css class to add to selected nodes
19697      */
19698     selectedClass : "x-view-selected",
19699      /**
19700      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19701      */
19702     emptyText : "",
19703     
19704     /**
19705      * @cfg {String} text to display on mask (default Loading)
19706      */
19707     mask : false,
19708     /**
19709      * @cfg {Boolean} multiSelect Allow multiple selection
19710      */
19711     multiSelect : false,
19712     /**
19713      * @cfg {Boolean} singleSelect Allow single selection
19714      */
19715     singleSelect:  false,
19716     
19717     /**
19718      * @cfg {Boolean} toggleSelect - selecting 
19719      */
19720     toggleSelect : false,
19721     
19722     /**
19723      * @cfg {Boolean} tickable - selecting 
19724      */
19725     tickable : false,
19726     
19727     /**
19728      * Returns the element this view is bound to.
19729      * @return {Roo.Element}
19730      */
19731     getEl : function(){
19732         return this.wrapEl;
19733     },
19734     
19735     
19736
19737     /**
19738      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19739      */
19740     refresh : function(){
19741         //Roo.log('refresh');
19742         var t = this.tpl;
19743         
19744         // if we are using something like 'domtemplate', then
19745         // the what gets used is:
19746         // t.applySubtemplate(NAME, data, wrapping data..)
19747         // the outer template then get' applied with
19748         //     the store 'extra data'
19749         // and the body get's added to the
19750         //      roo-name="data" node?
19751         //      <span class='roo-tpl-{name}'></span> ?????
19752         
19753         
19754         
19755         this.clearSelections();
19756         this.el.update("");
19757         var html = [];
19758         var records = this.store.getRange();
19759         if(records.length < 1) {
19760             
19761             // is this valid??  = should it render a template??
19762             
19763             this.el.update(this.emptyText);
19764             return;
19765         }
19766         var el = this.el;
19767         if (this.dataName) {
19768             this.el.update(t.apply(this.store.meta)); //????
19769             el = this.el.child('.roo-tpl-' + this.dataName);
19770         }
19771         
19772         for(var i = 0, len = records.length; i < len; i++){
19773             var data = this.prepareData(records[i].data, i, records[i]);
19774             this.fireEvent("preparedata", this, data, i, records[i]);
19775             
19776             var d = Roo.apply({}, data);
19777             
19778             if(this.tickable){
19779                 Roo.apply(d, {'roo-id' : Roo.id()});
19780                 
19781                 var _this = this;
19782             
19783                 Roo.each(this.parent.item, function(item){
19784                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19785                         return;
19786                     }
19787                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19788                 });
19789             }
19790             
19791             html[html.length] = Roo.util.Format.trim(
19792                 this.dataName ?
19793                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19794                     t.apply(d)
19795             );
19796         }
19797         
19798         
19799         
19800         el.update(html.join(""));
19801         this.nodes = el.dom.childNodes;
19802         this.updateIndexes(0);
19803     },
19804     
19805
19806     /**
19807      * Function to override to reformat the data that is sent to
19808      * the template for each node.
19809      * DEPRICATED - use the preparedata event handler.
19810      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19811      * a JSON object for an UpdateManager bound view).
19812      */
19813     prepareData : function(data, index, record)
19814     {
19815         this.fireEvent("preparedata", this, data, index, record);
19816         return data;
19817     },
19818
19819     onUpdate : function(ds, record){
19820         // Roo.log('on update');   
19821         this.clearSelections();
19822         var index = this.store.indexOf(record);
19823         var n = this.nodes[index];
19824         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19825         n.parentNode.removeChild(n);
19826         this.updateIndexes(index, index);
19827     },
19828
19829     
19830     
19831 // --------- FIXME     
19832     onAdd : function(ds, records, index)
19833     {
19834         //Roo.log(['on Add', ds, records, index] );        
19835         this.clearSelections();
19836         if(this.nodes.length == 0){
19837             this.refresh();
19838             return;
19839         }
19840         var n = this.nodes[index];
19841         for(var i = 0, len = records.length; i < len; i++){
19842             var d = this.prepareData(records[i].data, i, records[i]);
19843             if(n){
19844                 this.tpl.insertBefore(n, d);
19845             }else{
19846                 
19847                 this.tpl.append(this.el, d);
19848             }
19849         }
19850         this.updateIndexes(index);
19851     },
19852
19853     onRemove : function(ds, record, index){
19854        // Roo.log('onRemove');
19855         this.clearSelections();
19856         var el = this.dataName  ?
19857             this.el.child('.roo-tpl-' + this.dataName) :
19858             this.el; 
19859         
19860         el.dom.removeChild(this.nodes[index]);
19861         this.updateIndexes(index);
19862     },
19863
19864     /**
19865      * Refresh an individual node.
19866      * @param {Number} index
19867      */
19868     refreshNode : function(index){
19869         this.onUpdate(this.store, this.store.getAt(index));
19870     },
19871
19872     updateIndexes : function(startIndex, endIndex){
19873         var ns = this.nodes;
19874         startIndex = startIndex || 0;
19875         endIndex = endIndex || ns.length - 1;
19876         for(var i = startIndex; i <= endIndex; i++){
19877             ns[i].nodeIndex = i;
19878         }
19879     },
19880
19881     /**
19882      * Changes the data store this view uses and refresh the view.
19883      * @param {Store} store
19884      */
19885     setStore : function(store, initial){
19886         if(!initial && this.store){
19887             this.store.un("datachanged", this.refresh);
19888             this.store.un("add", this.onAdd);
19889             this.store.un("remove", this.onRemove);
19890             this.store.un("update", this.onUpdate);
19891             this.store.un("clear", this.refresh);
19892             this.store.un("beforeload", this.onBeforeLoad);
19893             this.store.un("load", this.onLoad);
19894             this.store.un("loadexception", this.onLoad);
19895         }
19896         if(store){
19897           
19898             store.on("datachanged", this.refresh, this);
19899             store.on("add", this.onAdd, this);
19900             store.on("remove", this.onRemove, this);
19901             store.on("update", this.onUpdate, this);
19902             store.on("clear", this.refresh, this);
19903             store.on("beforeload", this.onBeforeLoad, this);
19904             store.on("load", this.onLoad, this);
19905             store.on("loadexception", this.onLoad, this);
19906         }
19907         
19908         if(store){
19909             this.refresh();
19910         }
19911     },
19912     /**
19913      * onbeforeLoad - masks the loading area.
19914      *
19915      */
19916     onBeforeLoad : function(store,opts)
19917     {
19918          //Roo.log('onBeforeLoad');   
19919         if (!opts.add) {
19920             this.el.update("");
19921         }
19922         this.el.mask(this.mask ? this.mask : "Loading" ); 
19923     },
19924     onLoad : function ()
19925     {
19926         this.el.unmask();
19927     },
19928     
19929
19930     /**
19931      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19932      * @param {HTMLElement} node
19933      * @return {HTMLElement} The template node
19934      */
19935     findItemFromChild : function(node){
19936         var el = this.dataName  ?
19937             this.el.child('.roo-tpl-' + this.dataName,true) :
19938             this.el.dom; 
19939         
19940         if(!node || node.parentNode == el){
19941                     return node;
19942             }
19943             var p = node.parentNode;
19944             while(p && p != el){
19945             if(p.parentNode == el){
19946                 return p;
19947             }
19948             p = p.parentNode;
19949         }
19950             return null;
19951     },
19952
19953     /** @ignore */
19954     onClick : function(e){
19955         var item = this.findItemFromChild(e.getTarget());
19956         if(item){
19957             var index = this.indexOf(item);
19958             if(this.onItemClick(item, index, e) !== false){
19959                 this.fireEvent("click", this, index, item, e);
19960             }
19961         }else{
19962             this.clearSelections();
19963         }
19964     },
19965
19966     /** @ignore */
19967     onContextMenu : function(e){
19968         var item = this.findItemFromChild(e.getTarget());
19969         if(item){
19970             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19971         }
19972     },
19973
19974     /** @ignore */
19975     onDblClick : function(e){
19976         var item = this.findItemFromChild(e.getTarget());
19977         if(item){
19978             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19979         }
19980     },
19981
19982     onItemClick : function(item, index, e)
19983     {
19984         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19985             return false;
19986         }
19987         if (this.toggleSelect) {
19988             var m = this.isSelected(item) ? 'unselect' : 'select';
19989             //Roo.log(m);
19990             var _t = this;
19991             _t[m](item, true, false);
19992             return true;
19993         }
19994         if(this.multiSelect || this.singleSelect){
19995             if(this.multiSelect && e.shiftKey && this.lastSelection){
19996                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19997             }else{
19998                 this.select(item, this.multiSelect && e.ctrlKey);
19999                 this.lastSelection = item;
20000             }
20001             
20002             if(!this.tickable){
20003                 e.preventDefault();
20004             }
20005             
20006         }
20007         return true;
20008     },
20009
20010     /**
20011      * Get the number of selected nodes.
20012      * @return {Number}
20013      */
20014     getSelectionCount : function(){
20015         return this.selections.length;
20016     },
20017
20018     /**
20019      * Get the currently selected nodes.
20020      * @return {Array} An array of HTMLElements
20021      */
20022     getSelectedNodes : function(){
20023         return this.selections;
20024     },
20025
20026     /**
20027      * Get the indexes of the selected nodes.
20028      * @return {Array}
20029      */
20030     getSelectedIndexes : function(){
20031         var indexes = [], s = this.selections;
20032         for(var i = 0, len = s.length; i < len; i++){
20033             indexes.push(s[i].nodeIndex);
20034         }
20035         return indexes;
20036     },
20037
20038     /**
20039      * Clear all selections
20040      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20041      */
20042     clearSelections : function(suppressEvent){
20043         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20044             this.cmp.elements = this.selections;
20045             this.cmp.removeClass(this.selectedClass);
20046             this.selections = [];
20047             if(!suppressEvent){
20048                 this.fireEvent("selectionchange", this, this.selections);
20049             }
20050         }
20051     },
20052
20053     /**
20054      * Returns true if the passed node is selected
20055      * @param {HTMLElement/Number} node The node or node index
20056      * @return {Boolean}
20057      */
20058     isSelected : function(node){
20059         var s = this.selections;
20060         if(s.length < 1){
20061             return false;
20062         }
20063         node = this.getNode(node);
20064         return s.indexOf(node) !== -1;
20065     },
20066
20067     /**
20068      * Selects nodes.
20069      * @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
20070      * @param {Boolean} keepExisting (optional) true to keep existing selections
20071      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20072      */
20073     select : function(nodeInfo, keepExisting, suppressEvent){
20074         if(nodeInfo instanceof Array){
20075             if(!keepExisting){
20076                 this.clearSelections(true);
20077             }
20078             for(var i = 0, len = nodeInfo.length; i < len; i++){
20079                 this.select(nodeInfo[i], true, true);
20080             }
20081             return;
20082         } 
20083         var node = this.getNode(nodeInfo);
20084         if(!node || this.isSelected(node)){
20085             return; // already selected.
20086         }
20087         if(!keepExisting){
20088             this.clearSelections(true);
20089         }
20090         
20091         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20092             Roo.fly(node).addClass(this.selectedClass);
20093             this.selections.push(node);
20094             if(!suppressEvent){
20095                 this.fireEvent("selectionchange", this, this.selections);
20096             }
20097         }
20098         
20099         
20100     },
20101       /**
20102      * Unselects nodes.
20103      * @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
20104      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20105      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20106      */
20107     unselect : function(nodeInfo, keepExisting, suppressEvent)
20108     {
20109         if(nodeInfo instanceof Array){
20110             Roo.each(this.selections, function(s) {
20111                 this.unselect(s, nodeInfo);
20112             }, this);
20113             return;
20114         }
20115         var node = this.getNode(nodeInfo);
20116         if(!node || !this.isSelected(node)){
20117             //Roo.log("not selected");
20118             return; // not selected.
20119         }
20120         // fireevent???
20121         var ns = [];
20122         Roo.each(this.selections, function(s) {
20123             if (s == node ) {
20124                 Roo.fly(node).removeClass(this.selectedClass);
20125
20126                 return;
20127             }
20128             ns.push(s);
20129         },this);
20130         
20131         this.selections= ns;
20132         this.fireEvent("selectionchange", this, this.selections);
20133     },
20134
20135     /**
20136      * Gets a template node.
20137      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20138      * @return {HTMLElement} The node or null if it wasn't found
20139      */
20140     getNode : function(nodeInfo){
20141         if(typeof nodeInfo == "string"){
20142             return document.getElementById(nodeInfo);
20143         }else if(typeof nodeInfo == "number"){
20144             return this.nodes[nodeInfo];
20145         }
20146         return nodeInfo;
20147     },
20148
20149     /**
20150      * Gets a range template nodes.
20151      * @param {Number} startIndex
20152      * @param {Number} endIndex
20153      * @return {Array} An array of nodes
20154      */
20155     getNodes : function(start, end){
20156         var ns = this.nodes;
20157         start = start || 0;
20158         end = typeof end == "undefined" ? ns.length - 1 : end;
20159         var nodes = [];
20160         if(start <= end){
20161             for(var i = start; i <= end; i++){
20162                 nodes.push(ns[i]);
20163             }
20164         } else{
20165             for(var i = start; i >= end; i--){
20166                 nodes.push(ns[i]);
20167             }
20168         }
20169         return nodes;
20170     },
20171
20172     /**
20173      * Finds the index of the passed node
20174      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20175      * @return {Number} The index of the node or -1
20176      */
20177     indexOf : function(node){
20178         node = this.getNode(node);
20179         if(typeof node.nodeIndex == "number"){
20180             return node.nodeIndex;
20181         }
20182         var ns = this.nodes;
20183         for(var i = 0, len = ns.length; i < len; i++){
20184             if(ns[i] == node){
20185                 return i;
20186             }
20187         }
20188         return -1;
20189     }
20190 });
20191 /*
20192  * - LGPL
20193  *
20194  * based on jquery fullcalendar
20195  * 
20196  */
20197
20198 Roo.bootstrap = Roo.bootstrap || {};
20199 /**
20200  * @class Roo.bootstrap.Calendar
20201  * @extends Roo.bootstrap.Component
20202  * Bootstrap Calendar class
20203  * @cfg {Boolean} loadMask (true|false) default false
20204  * @cfg {Object} header generate the user specific header of the calendar, default false
20205
20206  * @constructor
20207  * Create a new Container
20208  * @param {Object} config The config object
20209  */
20210
20211
20212
20213 Roo.bootstrap.Calendar = function(config){
20214     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20215      this.addEvents({
20216         /**
20217              * @event select
20218              * Fires when a date is selected
20219              * @param {DatePicker} this
20220              * @param {Date} date The selected date
20221              */
20222         'select': true,
20223         /**
20224              * @event monthchange
20225              * Fires when the displayed month changes 
20226              * @param {DatePicker} this
20227              * @param {Date} date The selected month
20228              */
20229         'monthchange': true,
20230         /**
20231              * @event evententer
20232              * Fires when mouse over an event
20233              * @param {Calendar} this
20234              * @param {event} Event
20235              */
20236         'evententer': true,
20237         /**
20238              * @event eventleave
20239              * Fires when the mouse leaves an
20240              * @param {Calendar} this
20241              * @param {event}
20242              */
20243         'eventleave': true,
20244         /**
20245              * @event eventclick
20246              * Fires when the mouse click an
20247              * @param {Calendar} this
20248              * @param {event}
20249              */
20250         'eventclick': true
20251         
20252     });
20253
20254 };
20255
20256 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20257     
20258           /**
20259      * @cfg {Roo.data.Store} store
20260      * The data source for the calendar
20261      */
20262         store : false,
20263      /**
20264      * @cfg {Number} startDay
20265      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20266      */
20267     startDay : 0,
20268     
20269     loadMask : false,
20270     
20271     header : false,
20272       
20273     getAutoCreate : function(){
20274         
20275         
20276         var fc_button = function(name, corner, style, content ) {
20277             return Roo.apply({},{
20278                 tag : 'span',
20279                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20280                          (corner.length ?
20281                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20282                             ''
20283                         ),
20284                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20285                 unselectable: 'on'
20286             });
20287         };
20288         
20289         var header = {};
20290         
20291         if(!this.header){
20292             header = {
20293                 tag : 'table',
20294                 cls : 'fc-header',
20295                 style : 'width:100%',
20296                 cn : [
20297                     {
20298                         tag: 'tr',
20299                         cn : [
20300                             {
20301                                 tag : 'td',
20302                                 cls : 'fc-header-left',
20303                                 cn : [
20304                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20305                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20306                                     { tag: 'span', cls: 'fc-header-space' },
20307                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20308
20309
20310                                 ]
20311                             },
20312
20313                             {
20314                                 tag : 'td',
20315                                 cls : 'fc-header-center',
20316                                 cn : [
20317                                     {
20318                                         tag: 'span',
20319                                         cls: 'fc-header-title',
20320                                         cn : {
20321                                             tag: 'H2',
20322                                             html : 'month / year'
20323                                         }
20324                                     }
20325
20326                                 ]
20327                             },
20328                             {
20329                                 tag : 'td',
20330                                 cls : 'fc-header-right',
20331                                 cn : [
20332                               /*      fc_button('month', 'left', '', 'month' ),
20333                                     fc_button('week', '', '', 'week' ),
20334                                     fc_button('day', 'right', '', 'day' )
20335                                 */    
20336
20337                                 ]
20338                             }
20339
20340                         ]
20341                     }
20342                 ]
20343             };
20344         }
20345         
20346         header = this.header;
20347         
20348        
20349         var cal_heads = function() {
20350             var ret = [];
20351             // fixme - handle this.
20352             
20353             for (var i =0; i < Date.dayNames.length; i++) {
20354                 var d = Date.dayNames[i];
20355                 ret.push({
20356                     tag: 'th',
20357                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20358                     html : d.substring(0,3)
20359                 });
20360                 
20361             }
20362             ret[0].cls += ' fc-first';
20363             ret[6].cls += ' fc-last';
20364             return ret;
20365         };
20366         var cal_cell = function(n) {
20367             return  {
20368                 tag: 'td',
20369                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20370                 cn : [
20371                     {
20372                         cn : [
20373                             {
20374                                 cls: 'fc-day-number',
20375                                 html: 'D'
20376                             },
20377                             {
20378                                 cls: 'fc-day-content',
20379                              
20380                                 cn : [
20381                                      {
20382                                         style: 'position: relative;' // height: 17px;
20383                                     }
20384                                 ]
20385                             }
20386                             
20387                             
20388                         ]
20389                     }
20390                 ]
20391                 
20392             }
20393         };
20394         var cal_rows = function() {
20395             
20396             var ret = [];
20397             for (var r = 0; r < 6; r++) {
20398                 var row= {
20399                     tag : 'tr',
20400                     cls : 'fc-week',
20401                     cn : []
20402                 };
20403                 
20404                 for (var i =0; i < Date.dayNames.length; i++) {
20405                     var d = Date.dayNames[i];
20406                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20407
20408                 }
20409                 row.cn[0].cls+=' fc-first';
20410                 row.cn[0].cn[0].style = 'min-height:90px';
20411                 row.cn[6].cls+=' fc-last';
20412                 ret.push(row);
20413                 
20414             }
20415             ret[0].cls += ' fc-first';
20416             ret[4].cls += ' fc-prev-last';
20417             ret[5].cls += ' fc-last';
20418             return ret;
20419             
20420         };
20421         
20422         var cal_table = {
20423             tag: 'table',
20424             cls: 'fc-border-separate',
20425             style : 'width:100%',
20426             cellspacing  : 0,
20427             cn : [
20428                 { 
20429                     tag: 'thead',
20430                     cn : [
20431                         { 
20432                             tag: 'tr',
20433                             cls : 'fc-first fc-last',
20434                             cn : cal_heads()
20435                         }
20436                     ]
20437                 },
20438                 { 
20439                     tag: 'tbody',
20440                     cn : cal_rows()
20441                 }
20442                   
20443             ]
20444         };
20445          
20446          var cfg = {
20447             cls : 'fc fc-ltr',
20448             cn : [
20449                 header,
20450                 {
20451                     cls : 'fc-content',
20452                     style : "position: relative;",
20453                     cn : [
20454                         {
20455                             cls : 'fc-view fc-view-month fc-grid',
20456                             style : 'position: relative',
20457                             unselectable : 'on',
20458                             cn : [
20459                                 {
20460                                     cls : 'fc-event-container',
20461                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20462                                 },
20463                                 cal_table
20464                             ]
20465                         }
20466                     ]
20467     
20468                 }
20469            ] 
20470             
20471         };
20472         
20473          
20474         
20475         return cfg;
20476     },
20477     
20478     
20479     initEvents : function()
20480     {
20481         if(!this.store){
20482             throw "can not find store for calendar";
20483         }
20484         
20485         var mark = {
20486             tag: "div",
20487             cls:"x-dlg-mask",
20488             style: "text-align:center",
20489             cn: [
20490                 {
20491                     tag: "div",
20492                     style: "background-color:white;width:50%;margin:250 auto",
20493                     cn: [
20494                         {
20495                             tag: "img",
20496                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20497                         },
20498                         {
20499                             tag: "span",
20500                             html: "Loading"
20501                         }
20502                         
20503                     ]
20504                 }
20505             ]
20506         };
20507         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20508         
20509         var size = this.el.select('.fc-content', true).first().getSize();
20510         this.maskEl.setSize(size.width, size.height);
20511         this.maskEl.enableDisplayMode("block");
20512         if(!this.loadMask){
20513             this.maskEl.hide();
20514         }
20515         
20516         this.store = Roo.factory(this.store, Roo.data);
20517         this.store.on('load', this.onLoad, this);
20518         this.store.on('beforeload', this.onBeforeLoad, this);
20519         
20520         this.resize();
20521         
20522         this.cells = this.el.select('.fc-day',true);
20523         //Roo.log(this.cells);
20524         this.textNodes = this.el.query('.fc-day-number');
20525         this.cells.addClassOnOver('fc-state-hover');
20526         
20527         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20528         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20529         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20530         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20531         
20532         this.on('monthchange', this.onMonthChange, this);
20533         
20534         this.update(new Date().clearTime());
20535     },
20536     
20537     resize : function() {
20538         var sz  = this.el.getSize();
20539         
20540         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20541         this.el.select('.fc-day-content div',true).setHeight(34);
20542     },
20543     
20544     
20545     // private
20546     showPrevMonth : function(e){
20547         this.update(this.activeDate.add("mo", -1));
20548     },
20549     showToday : function(e){
20550         this.update(new Date().clearTime());
20551     },
20552     // private
20553     showNextMonth : function(e){
20554         this.update(this.activeDate.add("mo", 1));
20555     },
20556
20557     // private
20558     showPrevYear : function(){
20559         this.update(this.activeDate.add("y", -1));
20560     },
20561
20562     // private
20563     showNextYear : function(){
20564         this.update(this.activeDate.add("y", 1));
20565     },
20566
20567     
20568    // private
20569     update : function(date)
20570     {
20571         var vd = this.activeDate;
20572         this.activeDate = date;
20573 //        if(vd && this.el){
20574 //            var t = date.getTime();
20575 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20576 //                Roo.log('using add remove');
20577 //                
20578 //                this.fireEvent('monthchange', this, date);
20579 //                
20580 //                this.cells.removeClass("fc-state-highlight");
20581 //                this.cells.each(function(c){
20582 //                   if(c.dateValue == t){
20583 //                       c.addClass("fc-state-highlight");
20584 //                       setTimeout(function(){
20585 //                            try{c.dom.firstChild.focus();}catch(e){}
20586 //                       }, 50);
20587 //                       return false;
20588 //                   }
20589 //                   return true;
20590 //                });
20591 //                return;
20592 //            }
20593 //        }
20594         
20595         var days = date.getDaysInMonth();
20596         
20597         var firstOfMonth = date.getFirstDateOfMonth();
20598         var startingPos = firstOfMonth.getDay()-this.startDay;
20599         
20600         if(startingPos < this.startDay){
20601             startingPos += 7;
20602         }
20603         
20604         var pm = date.add(Date.MONTH, -1);
20605         var prevStart = pm.getDaysInMonth()-startingPos;
20606 //        
20607         this.cells = this.el.select('.fc-day',true);
20608         this.textNodes = this.el.query('.fc-day-number');
20609         this.cells.addClassOnOver('fc-state-hover');
20610         
20611         var cells = this.cells.elements;
20612         var textEls = this.textNodes;
20613         
20614         Roo.each(cells, function(cell){
20615             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20616         });
20617         
20618         days += startingPos;
20619
20620         // convert everything to numbers so it's fast
20621         var day = 86400000;
20622         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20623         //Roo.log(d);
20624         //Roo.log(pm);
20625         //Roo.log(prevStart);
20626         
20627         var today = new Date().clearTime().getTime();
20628         var sel = date.clearTime().getTime();
20629         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20630         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20631         var ddMatch = this.disabledDatesRE;
20632         var ddText = this.disabledDatesText;
20633         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20634         var ddaysText = this.disabledDaysText;
20635         var format = this.format;
20636         
20637         var setCellClass = function(cal, cell){
20638             cell.row = 0;
20639             cell.events = [];
20640             cell.more = [];
20641             //Roo.log('set Cell Class');
20642             cell.title = "";
20643             var t = d.getTime();
20644             
20645             //Roo.log(d);
20646             
20647             cell.dateValue = t;
20648             if(t == today){
20649                 cell.className += " fc-today";
20650                 cell.className += " fc-state-highlight";
20651                 cell.title = cal.todayText;
20652             }
20653             if(t == sel){
20654                 // disable highlight in other month..
20655                 //cell.className += " fc-state-highlight";
20656                 
20657             }
20658             // disabling
20659             if(t < min) {
20660                 cell.className = " fc-state-disabled";
20661                 cell.title = cal.minText;
20662                 return;
20663             }
20664             if(t > max) {
20665                 cell.className = " fc-state-disabled";
20666                 cell.title = cal.maxText;
20667                 return;
20668             }
20669             if(ddays){
20670                 if(ddays.indexOf(d.getDay()) != -1){
20671                     cell.title = ddaysText;
20672                     cell.className = " fc-state-disabled";
20673                 }
20674             }
20675             if(ddMatch && format){
20676                 var fvalue = d.dateFormat(format);
20677                 if(ddMatch.test(fvalue)){
20678                     cell.title = ddText.replace("%0", fvalue);
20679                     cell.className = " fc-state-disabled";
20680                 }
20681             }
20682             
20683             if (!cell.initialClassName) {
20684                 cell.initialClassName = cell.dom.className;
20685             }
20686             
20687             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20688         };
20689
20690         var i = 0;
20691         
20692         for(; i < startingPos; i++) {
20693             textEls[i].innerHTML = (++prevStart);
20694             d.setDate(d.getDate()+1);
20695             
20696             cells[i].className = "fc-past fc-other-month";
20697             setCellClass(this, cells[i]);
20698         }
20699         
20700         var intDay = 0;
20701         
20702         for(; i < days; i++){
20703             intDay = i - startingPos + 1;
20704             textEls[i].innerHTML = (intDay);
20705             d.setDate(d.getDate()+1);
20706             
20707             cells[i].className = ''; // "x-date-active";
20708             setCellClass(this, cells[i]);
20709         }
20710         var extraDays = 0;
20711         
20712         for(; i < 42; i++) {
20713             textEls[i].innerHTML = (++extraDays);
20714             d.setDate(d.getDate()+1);
20715             
20716             cells[i].className = "fc-future fc-other-month";
20717             setCellClass(this, cells[i]);
20718         }
20719         
20720         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20721         
20722         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20723         
20724         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20725         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20726         
20727         if(totalRows != 6){
20728             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20729             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20730         }
20731         
20732         this.fireEvent('monthchange', this, date);
20733         
20734         
20735         /*
20736         if(!this.internalRender){
20737             var main = this.el.dom.firstChild;
20738             var w = main.offsetWidth;
20739             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20740             Roo.fly(main).setWidth(w);
20741             this.internalRender = true;
20742             // opera does not respect the auto grow header center column
20743             // then, after it gets a width opera refuses to recalculate
20744             // without a second pass
20745             if(Roo.isOpera && !this.secondPass){
20746                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20747                 this.secondPass = true;
20748                 this.update.defer(10, this, [date]);
20749             }
20750         }
20751         */
20752         
20753     },
20754     
20755     findCell : function(dt) {
20756         dt = dt.clearTime().getTime();
20757         var ret = false;
20758         this.cells.each(function(c){
20759             //Roo.log("check " +c.dateValue + '?=' + dt);
20760             if(c.dateValue == dt){
20761                 ret = c;
20762                 return false;
20763             }
20764             return true;
20765         });
20766         
20767         return ret;
20768     },
20769     
20770     findCells : function(ev) {
20771         var s = ev.start.clone().clearTime().getTime();
20772        // Roo.log(s);
20773         var e= ev.end.clone().clearTime().getTime();
20774        // Roo.log(e);
20775         var ret = [];
20776         this.cells.each(function(c){
20777              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20778             
20779             if(c.dateValue > e){
20780                 return ;
20781             }
20782             if(c.dateValue < s){
20783                 return ;
20784             }
20785             ret.push(c);
20786         });
20787         
20788         return ret;    
20789     },
20790     
20791 //    findBestRow: function(cells)
20792 //    {
20793 //        var ret = 0;
20794 //        
20795 //        for (var i =0 ; i < cells.length;i++) {
20796 //            ret  = Math.max(cells[i].rows || 0,ret);
20797 //        }
20798 //        return ret;
20799 //        
20800 //    },
20801     
20802     
20803     addItem : function(ev)
20804     {
20805         // look for vertical location slot in
20806         var cells = this.findCells(ev);
20807         
20808 //        ev.row = this.findBestRow(cells);
20809         
20810         // work out the location.
20811         
20812         var crow = false;
20813         var rows = [];
20814         for(var i =0; i < cells.length; i++) {
20815             
20816             cells[i].row = cells[0].row;
20817             
20818             if(i == 0){
20819                 cells[i].row = cells[i].row + 1;
20820             }
20821             
20822             if (!crow) {
20823                 crow = {
20824                     start : cells[i],
20825                     end :  cells[i]
20826                 };
20827                 continue;
20828             }
20829             if (crow.start.getY() == cells[i].getY()) {
20830                 // on same row.
20831                 crow.end = cells[i];
20832                 continue;
20833             }
20834             // different row.
20835             rows.push(crow);
20836             crow = {
20837                 start: cells[i],
20838                 end : cells[i]
20839             };
20840             
20841         }
20842         
20843         rows.push(crow);
20844         ev.els = [];
20845         ev.rows = rows;
20846         ev.cells = cells;
20847         
20848         cells[0].events.push(ev);
20849         
20850         this.calevents.push(ev);
20851     },
20852     
20853     clearEvents: function() {
20854         
20855         if(!this.calevents){
20856             return;
20857         }
20858         
20859         Roo.each(this.cells.elements, function(c){
20860             c.row = 0;
20861             c.events = [];
20862             c.more = [];
20863         });
20864         
20865         Roo.each(this.calevents, function(e) {
20866             Roo.each(e.els, function(el) {
20867                 el.un('mouseenter' ,this.onEventEnter, this);
20868                 el.un('mouseleave' ,this.onEventLeave, this);
20869                 el.remove();
20870             },this);
20871         },this);
20872         
20873         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20874             e.remove();
20875         });
20876         
20877     },
20878     
20879     renderEvents: function()
20880     {   
20881         var _this = this;
20882         
20883         this.cells.each(function(c) {
20884             
20885             if(c.row < 5){
20886                 return;
20887             }
20888             
20889             var ev = c.events;
20890             
20891             var r = 4;
20892             if(c.row != c.events.length){
20893                 r = 4 - (4 - (c.row - c.events.length));
20894             }
20895             
20896             c.events = ev.slice(0, r);
20897             c.more = ev.slice(r);
20898             
20899             if(c.more.length && c.more.length == 1){
20900                 c.events.push(c.more.pop());
20901             }
20902             
20903             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20904             
20905         });
20906             
20907         this.cells.each(function(c) {
20908             
20909             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20910             
20911             
20912             for (var e = 0; e < c.events.length; e++){
20913                 var ev = c.events[e];
20914                 var rows = ev.rows;
20915                 
20916                 for(var i = 0; i < rows.length; i++) {
20917                 
20918                     // how many rows should it span..
20919
20920                     var  cfg = {
20921                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20922                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20923
20924                         unselectable : "on",
20925                         cn : [
20926                             {
20927                                 cls: 'fc-event-inner',
20928                                 cn : [
20929     //                                {
20930     //                                  tag:'span',
20931     //                                  cls: 'fc-event-time',
20932     //                                  html : cells.length > 1 ? '' : ev.time
20933     //                                },
20934                                     {
20935                                       tag:'span',
20936                                       cls: 'fc-event-title',
20937                                       html : String.format('{0}', ev.title)
20938                                     }
20939
20940
20941                                 ]
20942                             },
20943                             {
20944                                 cls: 'ui-resizable-handle ui-resizable-e',
20945                                 html : '&nbsp;&nbsp;&nbsp'
20946                             }
20947
20948                         ]
20949                     };
20950
20951                     if (i == 0) {
20952                         cfg.cls += ' fc-event-start';
20953                     }
20954                     if ((i+1) == rows.length) {
20955                         cfg.cls += ' fc-event-end';
20956                     }
20957
20958                     var ctr = _this.el.select('.fc-event-container',true).first();
20959                     var cg = ctr.createChild(cfg);
20960
20961                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20962                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20963
20964                     var r = (c.more.length) ? 1 : 0;
20965                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20966                     cg.setWidth(ebox.right - sbox.x -2);
20967
20968                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20969                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20970                     cg.on('click', _this.onEventClick, _this, ev);
20971
20972                     ev.els.push(cg);
20973                     
20974                 }
20975                 
20976             }
20977             
20978             
20979             if(c.more.length){
20980                 var  cfg = {
20981                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20982                     style : 'position: absolute',
20983                     unselectable : "on",
20984                     cn : [
20985                         {
20986                             cls: 'fc-event-inner',
20987                             cn : [
20988                                 {
20989                                   tag:'span',
20990                                   cls: 'fc-event-title',
20991                                   html : 'More'
20992                                 }
20993
20994
20995                             ]
20996                         },
20997                         {
20998                             cls: 'ui-resizable-handle ui-resizable-e',
20999                             html : '&nbsp;&nbsp;&nbsp'
21000                         }
21001
21002                     ]
21003                 };
21004
21005                 var ctr = _this.el.select('.fc-event-container',true).first();
21006                 var cg = ctr.createChild(cfg);
21007
21008                 var sbox = c.select('.fc-day-content',true).first().getBox();
21009                 var ebox = c.select('.fc-day-content',true).first().getBox();
21010                 //Roo.log(cg);
21011                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21012                 cg.setWidth(ebox.right - sbox.x -2);
21013
21014                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21015                 
21016             }
21017             
21018         });
21019         
21020         
21021         
21022     },
21023     
21024     onEventEnter: function (e, el,event,d) {
21025         this.fireEvent('evententer', this, el, event);
21026     },
21027     
21028     onEventLeave: function (e, el,event,d) {
21029         this.fireEvent('eventleave', this, el, event);
21030     },
21031     
21032     onEventClick: function (e, el,event,d) {
21033         this.fireEvent('eventclick', this, el, event);
21034     },
21035     
21036     onMonthChange: function () {
21037         this.store.load();
21038     },
21039     
21040     onMoreEventClick: function(e, el, more)
21041     {
21042         var _this = this;
21043         
21044         this.calpopover.placement = 'right';
21045         this.calpopover.setTitle('More');
21046         
21047         this.calpopover.setContent('');
21048         
21049         var ctr = this.calpopover.el.select('.popover-content', true).first();
21050         
21051         Roo.each(more, function(m){
21052             var cfg = {
21053                 cls : 'fc-event-hori fc-event-draggable',
21054                 html : m.title
21055             };
21056             var cg = ctr.createChild(cfg);
21057             
21058             cg.on('click', _this.onEventClick, _this, m);
21059         });
21060         
21061         this.calpopover.show(el);
21062         
21063         
21064     },
21065     
21066     onLoad: function () 
21067     {   
21068         this.calevents = [];
21069         var cal = this;
21070         
21071         if(this.store.getCount() > 0){
21072             this.store.data.each(function(d){
21073                cal.addItem({
21074                     id : d.data.id,
21075                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21076                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21077                     time : d.data.start_time,
21078                     title : d.data.title,
21079                     description : d.data.description,
21080                     venue : d.data.venue
21081                 });
21082             });
21083         }
21084         
21085         this.renderEvents();
21086         
21087         if(this.calevents.length && this.loadMask){
21088             this.maskEl.hide();
21089         }
21090     },
21091     
21092     onBeforeLoad: function()
21093     {
21094         this.clearEvents();
21095         if(this.loadMask){
21096             this.maskEl.show();
21097         }
21098     }
21099 });
21100
21101  
21102  /*
21103  * - LGPL
21104  *
21105  * element
21106  * 
21107  */
21108
21109 /**
21110  * @class Roo.bootstrap.Popover
21111  * @extends Roo.bootstrap.Component
21112  * @builder-top
21113  * @children Roo.bootstrap.Component
21114  * Bootstrap Popover class
21115  * @cfg {String} html contents of the popover   (or false to use children..)
21116  * @cfg {String} title of popover (or false to hide)
21117  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21118  * @cfg {String} trigger click || hover (or false to trigger manually)
21119  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21120  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21121  *      - if false and it has a 'parent' then it will be automatically added to that element
21122  *      - if string - Roo.get  will be called 
21123  * @cfg {Number} delay - delay before showing
21124  
21125  * @constructor
21126  * Create a new Popover
21127  * @param {Object} config The config object
21128  */
21129
21130 Roo.bootstrap.Popover = function(config){
21131     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21132     
21133     this.addEvents({
21134         // raw events
21135          /**
21136          * @event show
21137          * After the popover show
21138          * 
21139          * @param {Roo.bootstrap.Popover} this
21140          */
21141         "show" : true,
21142         /**
21143          * @event hide
21144          * After the popover hide
21145          * 
21146          * @param {Roo.bootstrap.Popover} this
21147          */
21148         "hide" : true
21149     });
21150 };
21151
21152 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21153     
21154     title: false,
21155     html: false,
21156     
21157     placement : 'right',
21158     trigger : 'hover', // hover
21159     modal : false,
21160     delay : 0,
21161     
21162     over: false,
21163     
21164     can_build_overlaid : false,
21165     
21166     maskEl : false, // the mask element
21167     headerEl : false,
21168     contentEl : false,
21169     alignEl : false, // when show is called with an element - this get's stored.
21170     
21171     getChildContainer : function()
21172     {
21173         return this.contentEl;
21174         
21175     },
21176     getPopoverHeader : function()
21177     {
21178         this.title = true; // flag not to hide it..
21179         this.headerEl.addClass('p-0');
21180         return this.headerEl
21181     },
21182     
21183     
21184     getAutoCreate : function(){
21185          
21186         var cfg = {
21187            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21188            style: 'display:block',
21189            cn : [
21190                 {
21191                     cls : 'arrow'
21192                 },
21193                 {
21194                     cls : 'popover-inner ',
21195                     cn : [
21196                         {
21197                             tag: 'h3',
21198                             cls: 'popover-title popover-header',
21199                             html : this.title === false ? '' : this.title
21200                         },
21201                         {
21202                             cls : 'popover-content popover-body '  + (this.cls || ''),
21203                             html : this.html || ''
21204                         }
21205                     ]
21206                     
21207                 }
21208            ]
21209         };
21210         
21211         return cfg;
21212     },
21213     /**
21214      * @param {string} the title
21215      */
21216     setTitle: function(str)
21217     {
21218         this.title = str;
21219         if (this.el) {
21220             this.headerEl.dom.innerHTML = str;
21221         }
21222         
21223     },
21224     /**
21225      * @param {string} the body content
21226      */
21227     setContent: function(str)
21228     {
21229         this.html = str;
21230         if (this.contentEl) {
21231             this.contentEl.dom.innerHTML = str;
21232         }
21233         
21234     },
21235     // as it get's added to the bottom of the page.
21236     onRender : function(ct, position)
21237     {
21238         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21239         
21240         
21241         
21242         if(!this.el){
21243             var cfg = Roo.apply({},  this.getAutoCreate());
21244             cfg.id = Roo.id();
21245             
21246             if (this.cls) {
21247                 cfg.cls += ' ' + this.cls;
21248             }
21249             if (this.style) {
21250                 cfg.style = this.style;
21251             }
21252             //Roo.log("adding to ");
21253             this.el = Roo.get(document.body).createChild(cfg, position);
21254 //            Roo.log(this.el);
21255         }
21256         
21257         this.contentEl = this.el.select('.popover-content',true).first();
21258         this.headerEl =  this.el.select('.popover-title',true).first();
21259         
21260         var nitems = [];
21261         if(typeof(this.items) != 'undefined'){
21262             var items = this.items;
21263             delete this.items;
21264
21265             for(var i =0;i < items.length;i++) {
21266                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21267             }
21268         }
21269
21270         this.items = nitems;
21271         
21272         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21273         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21274         
21275         
21276         
21277         this.initEvents();
21278     },
21279     
21280     resizeMask : function()
21281     {
21282         this.maskEl.setSize(
21283             Roo.lib.Dom.getViewWidth(true),
21284             Roo.lib.Dom.getViewHeight(true)
21285         );
21286     },
21287     
21288     initEvents : function()
21289     {
21290         
21291         if (!this.modal) { 
21292             Roo.bootstrap.Popover.register(this);
21293         }
21294          
21295         this.arrowEl = this.el.select('.arrow',true).first();
21296         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21297         this.el.enableDisplayMode('block');
21298         this.el.hide();
21299  
21300         
21301         if (this.over === false && !this.parent()) {
21302             return; 
21303         }
21304         if (this.triggers === false) {
21305             return;
21306         }
21307          
21308         // support parent
21309         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21310         var triggers = this.trigger ? this.trigger.split(' ') : [];
21311         Roo.each(triggers, function(trigger) {
21312         
21313             if (trigger == 'click') {
21314                 on_el.on('click', this.toggle, this);
21315             } else if (trigger != 'manual') {
21316                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21317                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21318       
21319                 on_el.on(eventIn  ,this.enter, this);
21320                 on_el.on(eventOut, this.leave, this);
21321             }
21322         }, this);
21323     },
21324     
21325     
21326     // private
21327     timeout : null,
21328     hoverState : null,
21329     
21330     toggle : function () {
21331         this.hoverState == 'in' ? this.leave() : this.enter();
21332     },
21333     
21334     enter : function () {
21335         
21336         clearTimeout(this.timeout);
21337     
21338         this.hoverState = 'in';
21339     
21340         if (!this.delay || !this.delay.show) {
21341             this.show();
21342             return;
21343         }
21344         var _t = this;
21345         this.timeout = setTimeout(function () {
21346             if (_t.hoverState == 'in') {
21347                 _t.show();
21348             }
21349         }, this.delay.show)
21350     },
21351     
21352     leave : function() {
21353         clearTimeout(this.timeout);
21354     
21355         this.hoverState = 'out';
21356     
21357         if (!this.delay || !this.delay.hide) {
21358             this.hide();
21359             return;
21360         }
21361         var _t = this;
21362         this.timeout = setTimeout(function () {
21363             if (_t.hoverState == 'out') {
21364                 _t.hide();
21365             }
21366         }, this.delay.hide)
21367     },
21368     /**
21369      * Show the popover
21370      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21371      * @param {string} (left|right|top|bottom) position
21372      */
21373     show : function (on_el, placement)
21374     {
21375         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21376         on_el = on_el || false; // default to false
21377          
21378         if (!on_el) {
21379             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21380                 on_el = this.parent().el;
21381             } else if (this.over) {
21382                 on_el = Roo.get(this.over);
21383             }
21384             
21385         }
21386         
21387         this.alignEl = Roo.get( on_el );
21388
21389         if (!this.el) {
21390             this.render(document.body);
21391         }
21392         
21393         
21394          
21395         
21396         if (this.title === false) {
21397             this.headerEl.hide();
21398         }
21399         
21400        
21401         this.el.show();
21402         this.el.dom.style.display = 'block';
21403          
21404  
21405         if (this.alignEl) {
21406             this.updatePosition(this.placement, true);
21407              
21408         } else {
21409             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21410             var es = this.el.getSize();
21411             var x = Roo.lib.Dom.getViewWidth()/2;
21412             var y = Roo.lib.Dom.getViewHeight()/2;
21413             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21414             
21415         }
21416
21417         
21418         //var arrow = this.el.select('.arrow',true).first();
21419         //arrow.set(align[2], 
21420         
21421         this.el.addClass('in');
21422         
21423          
21424         
21425         this.hoverState = 'in';
21426         
21427         if (this.modal) {
21428             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21429             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21430             this.maskEl.dom.style.display = 'block';
21431             this.maskEl.addClass('show');
21432         }
21433         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21434  
21435         this.fireEvent('show', this);
21436         
21437     },
21438     /**
21439      * fire this manually after loading a grid in the table for example
21440      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21441      * @param {Boolean} try and move it if we cant get right position.
21442      */
21443     updatePosition : function(placement, try_move)
21444     {
21445         // allow for calling with no parameters
21446         placement = placement   ? placement :  this.placement;
21447         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21448         
21449         this.el.removeClass([
21450             'fade','top','bottom', 'left', 'right','in',
21451             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21452         ]);
21453         this.el.addClass(placement + ' bs-popover-' + placement);
21454         
21455         if (!this.alignEl ) {
21456             return false;
21457         }
21458         
21459         switch (placement) {
21460             case 'right':
21461                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21462                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21463                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21464                     //normal display... or moved up/down.
21465                     this.el.setXY(offset);
21466                     var xy = this.alignEl.getAnchorXY('tr', false);
21467                     xy[0]+=2;xy[1]+=5;
21468                     this.arrowEl.setXY(xy);
21469                     return true;
21470                 }
21471                 // continue through...
21472                 return this.updatePosition('left', false);
21473                 
21474             
21475             case 'left':
21476                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21477                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21478                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21479                     //normal display... or moved up/down.
21480                     this.el.setXY(offset);
21481                     var xy = this.alignEl.getAnchorXY('tl', false);
21482                     xy[0]-=10;xy[1]+=5; // << fix me
21483                     this.arrowEl.setXY(xy);
21484                     return true;
21485                 }
21486                 // call self...
21487                 return this.updatePosition('right', false);
21488             
21489             case 'top':
21490                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21491                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21492                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21493                     //normal display... or moved up/down.
21494                     this.el.setXY(offset);
21495                     var xy = this.alignEl.getAnchorXY('t', false);
21496                     xy[1]-=10; // << fix me
21497                     this.arrowEl.setXY(xy);
21498                     return true;
21499                 }
21500                 // fall through
21501                return this.updatePosition('bottom', false);
21502             
21503             case 'bottom':
21504                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21505                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21506                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21507                     //normal display... or moved up/down.
21508                     this.el.setXY(offset);
21509                     var xy = this.alignEl.getAnchorXY('b', false);
21510                      xy[1]+=2; // << fix me
21511                     this.arrowEl.setXY(xy);
21512                     return true;
21513                 }
21514                 // fall through
21515                 return this.updatePosition('top', false);
21516                 
21517             
21518         }
21519         
21520         
21521         return false;
21522     },
21523     
21524     hide : function()
21525     {
21526         this.el.setXY([0,0]);
21527         this.el.removeClass('in');
21528         this.el.hide();
21529         this.hoverState = null;
21530         this.maskEl.hide(); // always..
21531         this.fireEvent('hide', this);
21532     }
21533     
21534 });
21535
21536
21537 Roo.apply(Roo.bootstrap.Popover, {
21538
21539     alignment : {
21540         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21541         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21542         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21543         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21544     },
21545     
21546     zIndex : 20001,
21547
21548     clickHander : false,
21549     
21550     
21551
21552     onMouseDown : function(e)
21553     {
21554         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21555             /// what is nothing is showing..
21556             this.hideAll();
21557         }
21558          
21559     },
21560     
21561     
21562     popups : [],
21563     
21564     register : function(popup)
21565     {
21566         if (!Roo.bootstrap.Popover.clickHandler) {
21567             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21568         }
21569         // hide other popups.
21570         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21571         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21572         this.hideAll(); //<< why?
21573         //this.popups.push(popup);
21574     },
21575     hideAll : function()
21576     {
21577         this.popups.forEach(function(p) {
21578             p.hide();
21579         });
21580     },
21581     onShow : function() {
21582         Roo.bootstrap.Popover.popups.push(this);
21583     },
21584     onHide : function() {
21585         Roo.bootstrap.Popover.popups.remove(this);
21586     } 
21587
21588 });/*
21589  * - LGPL
21590  *
21591  * Card header - holder for the card header elements.
21592  * 
21593  */
21594
21595 /**
21596  * @class Roo.bootstrap.PopoverNav
21597  * @extends Roo.bootstrap.NavGroup
21598  * Bootstrap Popover header navigation class
21599  * @constructor
21600  * Create a new Popover Header Navigation 
21601  * @param {Object} config The config object
21602  */
21603
21604 Roo.bootstrap.PopoverNav = function(config){
21605     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21606 };
21607
21608 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21609     
21610     
21611     container_method : 'getPopoverHeader' 
21612     
21613      
21614     
21615     
21616    
21617 });
21618
21619  
21620
21621  /*
21622  * - LGPL
21623  *
21624  * Progress
21625  * 
21626  */
21627
21628 /**
21629  * @class Roo.bootstrap.Progress
21630  * @extends Roo.bootstrap.Component
21631  * @children Roo.bootstrap.ProgressBar
21632  * Bootstrap Progress class
21633  * @cfg {Boolean} striped striped of the progress bar
21634  * @cfg {Boolean} active animated of the progress bar
21635  * 
21636  * 
21637  * @constructor
21638  * Create a new Progress
21639  * @param {Object} config The config object
21640  */
21641
21642 Roo.bootstrap.Progress = function(config){
21643     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21644 };
21645
21646 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21647     
21648     striped : false,
21649     active: false,
21650     
21651     getAutoCreate : function(){
21652         var cfg = {
21653             tag: 'div',
21654             cls: 'progress'
21655         };
21656         
21657         
21658         if(this.striped){
21659             cfg.cls += ' progress-striped';
21660         }
21661       
21662         if(this.active){
21663             cfg.cls += ' active';
21664         }
21665         
21666         
21667         return cfg;
21668     }
21669    
21670 });
21671
21672  
21673
21674  /*
21675  * - LGPL
21676  *
21677  * ProgressBar
21678  * 
21679  */
21680
21681 /**
21682  * @class Roo.bootstrap.ProgressBar
21683  * @extends Roo.bootstrap.Component
21684  * Bootstrap ProgressBar class
21685  * @cfg {Number} aria_valuenow aria-value now
21686  * @cfg {Number} aria_valuemin aria-value min
21687  * @cfg {Number} aria_valuemax aria-value max
21688  * @cfg {String} label label for the progress bar
21689  * @cfg {String} panel (success | info | warning | danger )
21690  * @cfg {String} role role of the progress bar
21691  * @cfg {String} sr_only text
21692  * 
21693  * 
21694  * @constructor
21695  * Create a new ProgressBar
21696  * @param {Object} config The config object
21697  */
21698
21699 Roo.bootstrap.ProgressBar = function(config){
21700     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21701 };
21702
21703 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21704     
21705     aria_valuenow : 0,
21706     aria_valuemin : 0,
21707     aria_valuemax : 100,
21708     label : false,
21709     panel : false,
21710     role : false,
21711     sr_only: false,
21712     
21713     getAutoCreate : function()
21714     {
21715         
21716         var cfg = {
21717             tag: 'div',
21718             cls: 'progress-bar',
21719             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21720         };
21721         
21722         if(this.sr_only){
21723             cfg.cn = {
21724                 tag: 'span',
21725                 cls: 'sr-only',
21726                 html: this.sr_only
21727             }
21728         }
21729         
21730         if(this.role){
21731             cfg.role = this.role;
21732         }
21733         
21734         if(this.aria_valuenow){
21735             cfg['aria-valuenow'] = this.aria_valuenow;
21736         }
21737         
21738         if(this.aria_valuemin){
21739             cfg['aria-valuemin'] = this.aria_valuemin;
21740         }
21741         
21742         if(this.aria_valuemax){
21743             cfg['aria-valuemax'] = this.aria_valuemax;
21744         }
21745         
21746         if(this.label && !this.sr_only){
21747             cfg.html = this.label;
21748         }
21749         
21750         if(this.panel){
21751             cfg.cls += ' progress-bar-' + this.panel;
21752         }
21753         
21754         return cfg;
21755     },
21756     
21757     update : function(aria_valuenow)
21758     {
21759         this.aria_valuenow = aria_valuenow;
21760         
21761         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21762     }
21763    
21764 });
21765
21766  
21767
21768  /**
21769  * @class Roo.bootstrap.TabGroup
21770  * @extends Roo.bootstrap.Column
21771  * @children Roo.bootstrap.TabPanel
21772  * Bootstrap Column class
21773  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21774  * @cfg {Boolean} carousel true to make the group behave like a carousel
21775  * @cfg {Boolean} bullets show bullets for the panels
21776  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21777  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21778  * @cfg {Boolean} showarrow (true|false) show arrow default true
21779  * 
21780  * @constructor
21781  * Create a new TabGroup
21782  * @param {Object} config The config object
21783  */
21784
21785 Roo.bootstrap.TabGroup = function(config){
21786     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21787     if (!this.navId) {
21788         this.navId = Roo.id();
21789     }
21790     this.tabs = [];
21791     Roo.bootstrap.TabGroup.register(this);
21792     
21793 };
21794
21795 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21796     
21797     carousel : false,
21798     transition : false,
21799     bullets : 0,
21800     timer : 0,
21801     autoslide : false,
21802     slideFn : false,
21803     slideOnTouch : false,
21804     showarrow : true,
21805     
21806     getAutoCreate : function()
21807     {
21808         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21809         
21810         cfg.cls += ' tab-content';
21811         
21812         if (this.carousel) {
21813             cfg.cls += ' carousel slide';
21814             
21815             cfg.cn = [{
21816                cls : 'carousel-inner',
21817                cn : []
21818             }];
21819         
21820             if(this.bullets  && !Roo.isTouch){
21821                 
21822                 var bullets = {
21823                     cls : 'carousel-bullets',
21824                     cn : []
21825                 };
21826                
21827                 if(this.bullets_cls){
21828                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21829                 }
21830                 
21831                 bullets.cn.push({
21832                     cls : 'clear'
21833                 });
21834                 
21835                 cfg.cn[0].cn.push(bullets);
21836             }
21837             
21838             if(this.showarrow){
21839                 cfg.cn[0].cn.push({
21840                     tag : 'div',
21841                     class : 'carousel-arrow',
21842                     cn : [
21843                         {
21844                             tag : 'div',
21845                             class : 'carousel-prev',
21846                             cn : [
21847                                 {
21848                                     tag : 'i',
21849                                     class : 'fa fa-chevron-left'
21850                                 }
21851                             ]
21852                         },
21853                         {
21854                             tag : 'div',
21855                             class : 'carousel-next',
21856                             cn : [
21857                                 {
21858                                     tag : 'i',
21859                                     class : 'fa fa-chevron-right'
21860                                 }
21861                             ]
21862                         }
21863                     ]
21864                 });
21865             }
21866             
21867         }
21868         
21869         return cfg;
21870     },
21871     
21872     initEvents:  function()
21873     {
21874 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21875 //            this.el.on("touchstart", this.onTouchStart, this);
21876 //        }
21877         
21878         if(this.autoslide){
21879             var _this = this;
21880             
21881             this.slideFn = window.setInterval(function() {
21882                 _this.showPanelNext();
21883             }, this.timer);
21884         }
21885         
21886         if(this.showarrow){
21887             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21888             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21889         }
21890         
21891         
21892     },
21893     
21894 //    onTouchStart : function(e, el, o)
21895 //    {
21896 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21897 //            return;
21898 //        }
21899 //        
21900 //        this.showPanelNext();
21901 //    },
21902     
21903     
21904     getChildContainer : function()
21905     {
21906         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21907     },
21908     
21909     /**
21910     * register a Navigation item
21911     * @param {Roo.bootstrap.NavItem} the navitem to add
21912     */
21913     register : function(item)
21914     {
21915         this.tabs.push( item);
21916         item.navId = this.navId; // not really needed..
21917         this.addBullet();
21918     
21919     },
21920     
21921     getActivePanel : function()
21922     {
21923         var r = false;
21924         Roo.each(this.tabs, function(t) {
21925             if (t.active) {
21926                 r = t;
21927                 return false;
21928             }
21929             return null;
21930         });
21931         return r;
21932         
21933     },
21934     getPanelByName : function(n)
21935     {
21936         var r = false;
21937         Roo.each(this.tabs, function(t) {
21938             if (t.tabId == n) {
21939                 r = t;
21940                 return false;
21941             }
21942             return null;
21943         });
21944         return r;
21945     },
21946     indexOfPanel : function(p)
21947     {
21948         var r = false;
21949         Roo.each(this.tabs, function(t,i) {
21950             if (t.tabId == p.tabId) {
21951                 r = i;
21952                 return false;
21953             }
21954             return null;
21955         });
21956         return r;
21957     },
21958     /**
21959      * show a specific panel
21960      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21961      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21962      */
21963     showPanel : function (pan)
21964     {
21965         if(this.transition || typeof(pan) == 'undefined'){
21966             Roo.log("waiting for the transitionend");
21967             return false;
21968         }
21969         
21970         if (typeof(pan) == 'number') {
21971             pan = this.tabs[pan];
21972         }
21973         
21974         if (typeof(pan) == 'string') {
21975             pan = this.getPanelByName(pan);
21976         }
21977         
21978         var cur = this.getActivePanel();
21979         
21980         if(!pan || !cur){
21981             Roo.log('pan or acitve pan is undefined');
21982             return false;
21983         }
21984         
21985         if (pan.tabId == this.getActivePanel().tabId) {
21986             return true;
21987         }
21988         
21989         if (false === cur.fireEvent('beforedeactivate')) {
21990             return false;
21991         }
21992         
21993         if(this.bullets > 0 && !Roo.isTouch){
21994             this.setActiveBullet(this.indexOfPanel(pan));
21995         }
21996         
21997         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21998             
21999             //class="carousel-item carousel-item-next carousel-item-left"
22000             
22001             this.transition = true;
22002             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22003             var lr = dir == 'next' ? 'left' : 'right';
22004             pan.el.addClass(dir); // or prev
22005             pan.el.addClass('carousel-item-' + dir); // or prev
22006             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22007             cur.el.addClass(lr); // or right
22008             pan.el.addClass(lr);
22009             cur.el.addClass('carousel-item-' +lr); // or right
22010             pan.el.addClass('carousel-item-' +lr);
22011             
22012             
22013             var _this = this;
22014             cur.el.on('transitionend', function() {
22015                 Roo.log("trans end?");
22016                 
22017                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22018                 pan.setActive(true);
22019                 
22020                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22021                 cur.setActive(false);
22022                 
22023                 _this.transition = false;
22024                 
22025             }, this, { single:  true } );
22026             
22027             return true;
22028         }
22029         
22030         cur.setActive(false);
22031         pan.setActive(true);
22032         
22033         return true;
22034         
22035     },
22036     showPanelNext : function()
22037     {
22038         var i = this.indexOfPanel(this.getActivePanel());
22039         
22040         if (i >= this.tabs.length - 1 && !this.autoslide) {
22041             return;
22042         }
22043         
22044         if (i >= this.tabs.length - 1 && this.autoslide) {
22045             i = -1;
22046         }
22047         
22048         this.showPanel(this.tabs[i+1]);
22049     },
22050     
22051     showPanelPrev : function()
22052     {
22053         var i = this.indexOfPanel(this.getActivePanel());
22054         
22055         if (i  < 1 && !this.autoslide) {
22056             return;
22057         }
22058         
22059         if (i < 1 && this.autoslide) {
22060             i = this.tabs.length;
22061         }
22062         
22063         this.showPanel(this.tabs[i-1]);
22064     },
22065     
22066     
22067     addBullet: function()
22068     {
22069         if(!this.bullets || Roo.isTouch){
22070             return;
22071         }
22072         var ctr = this.el.select('.carousel-bullets',true).first();
22073         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22074         var bullet = ctr.createChild({
22075             cls : 'bullet bullet-' + i
22076         },ctr.dom.lastChild);
22077         
22078         
22079         var _this = this;
22080         
22081         bullet.on('click', (function(e, el, o, ii, t){
22082
22083             e.preventDefault();
22084
22085             this.showPanel(ii);
22086
22087             if(this.autoslide && this.slideFn){
22088                 clearInterval(this.slideFn);
22089                 this.slideFn = window.setInterval(function() {
22090                     _this.showPanelNext();
22091                 }, this.timer);
22092             }
22093
22094         }).createDelegate(this, [i, bullet], true));
22095                 
22096         
22097     },
22098      
22099     setActiveBullet : function(i)
22100     {
22101         if(Roo.isTouch){
22102             return;
22103         }
22104         
22105         Roo.each(this.el.select('.bullet', true).elements, function(el){
22106             el.removeClass('selected');
22107         });
22108
22109         var bullet = this.el.select('.bullet-' + i, true).first();
22110         
22111         if(!bullet){
22112             return;
22113         }
22114         
22115         bullet.addClass('selected');
22116     }
22117     
22118     
22119   
22120 });
22121
22122  
22123
22124  
22125  
22126 Roo.apply(Roo.bootstrap.TabGroup, {
22127     
22128     groups: {},
22129      /**
22130     * register a Navigation Group
22131     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22132     */
22133     register : function(navgrp)
22134     {
22135         this.groups[navgrp.navId] = navgrp;
22136         
22137     },
22138     /**
22139     * fetch a Navigation Group based on the navigation ID
22140     * if one does not exist , it will get created.
22141     * @param {string} the navgroup to add
22142     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22143     */
22144     get: function(navId) {
22145         if (typeof(this.groups[navId]) == 'undefined') {
22146             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22147         }
22148         return this.groups[navId] ;
22149     }
22150     
22151     
22152     
22153 });
22154
22155  /*
22156  * - LGPL
22157  *
22158  * TabPanel
22159  * 
22160  */
22161
22162 /**
22163  * @class Roo.bootstrap.TabPanel
22164  * @extends Roo.bootstrap.Component
22165  * @children Roo.bootstrap.Component
22166  * Bootstrap TabPanel class
22167  * @cfg {Boolean} active panel active
22168  * @cfg {String} html panel content
22169  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22170  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22171  * @cfg {String} href click to link..
22172  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22173  * 
22174  * 
22175  * @constructor
22176  * Create a new TabPanel
22177  * @param {Object} config The config object
22178  */
22179
22180 Roo.bootstrap.TabPanel = function(config){
22181     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22182     this.addEvents({
22183         /**
22184              * @event changed
22185              * Fires when the active status changes
22186              * @param {Roo.bootstrap.TabPanel} this
22187              * @param {Boolean} state the new state
22188             
22189          */
22190         'changed': true,
22191         /**
22192              * @event beforedeactivate
22193              * Fires before a tab is de-activated - can be used to do validation on a form.
22194              * @param {Roo.bootstrap.TabPanel} this
22195              * @return {Boolean} false if there is an error
22196             
22197          */
22198         'beforedeactivate': true
22199      });
22200     
22201     this.tabId = this.tabId || Roo.id();
22202   
22203 };
22204
22205 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22206     
22207     active: false,
22208     html: false,
22209     tabId: false,
22210     navId : false,
22211     href : '',
22212     touchSlide : false,
22213     getAutoCreate : function(){
22214         
22215         
22216         var cfg = {
22217             tag: 'div',
22218             // item is needed for carousel - not sure if it has any effect otherwise
22219             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22220             html: this.html || ''
22221         };
22222         
22223         if(this.active){
22224             cfg.cls += ' active';
22225         }
22226         
22227         if(this.tabId){
22228             cfg.tabId = this.tabId;
22229         }
22230         
22231         
22232         
22233         return cfg;
22234     },
22235     
22236     initEvents:  function()
22237     {
22238         var p = this.parent();
22239         
22240         this.navId = this.navId || p.navId;
22241         
22242         if (typeof(this.navId) != 'undefined') {
22243             // not really needed.. but just in case.. parent should be a NavGroup.
22244             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22245             
22246             tg.register(this);
22247             
22248             var i = tg.tabs.length - 1;
22249             
22250             if(this.active && tg.bullets > 0 && i < tg.bullets){
22251                 tg.setActiveBullet(i);
22252             }
22253         }
22254         
22255         this.el.on('click', this.onClick, this);
22256         
22257         if(Roo.isTouch && this.touchSlide){
22258             this.el.on("touchstart", this.onTouchStart, this);
22259             this.el.on("touchmove", this.onTouchMove, this);
22260             this.el.on("touchend", this.onTouchEnd, this);
22261         }
22262         
22263     },
22264     
22265     onRender : function(ct, position)
22266     {
22267         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22268     },
22269     
22270     setActive : function(state)
22271     {
22272         Roo.log("panel - set active " + this.tabId + "=" + state);
22273         
22274         this.active = state;
22275         if (!state) {
22276             this.el.removeClass('active');
22277             
22278         } else  if (!this.el.hasClass('active')) {
22279             this.el.addClass('active');
22280         }
22281         
22282         this.fireEvent('changed', this, state);
22283     },
22284     
22285     onClick : function(e)
22286     {
22287         e.preventDefault();
22288         
22289         if(!this.href.length){
22290             return;
22291         }
22292         
22293         window.location.href = this.href;
22294     },
22295     
22296     startX : 0,
22297     startY : 0,
22298     endX : 0,
22299     endY : 0,
22300     swiping : false,
22301     
22302     onTouchStart : function(e)
22303     {
22304         this.swiping = false;
22305         
22306         this.startX = e.browserEvent.touches[0].clientX;
22307         this.startY = e.browserEvent.touches[0].clientY;
22308     },
22309     
22310     onTouchMove : function(e)
22311     {
22312         this.swiping = true;
22313         
22314         this.endX = e.browserEvent.touches[0].clientX;
22315         this.endY = e.browserEvent.touches[0].clientY;
22316     },
22317     
22318     onTouchEnd : function(e)
22319     {
22320         if(!this.swiping){
22321             this.onClick(e);
22322             return;
22323         }
22324         
22325         var tabGroup = this.parent();
22326         
22327         if(this.endX > this.startX){ // swiping right
22328             tabGroup.showPanelPrev();
22329             return;
22330         }
22331         
22332         if(this.startX > this.endX){ // swiping left
22333             tabGroup.showPanelNext();
22334             return;
22335         }
22336     }
22337     
22338     
22339 });
22340  
22341
22342  
22343
22344  /*
22345  * - LGPL
22346  *
22347  * DateField
22348  * 
22349  */
22350
22351 /**
22352  * @class Roo.bootstrap.DateField
22353  * @extends Roo.bootstrap.Input
22354  * Bootstrap DateField class
22355  * @cfg {Number} weekStart default 0
22356  * @cfg {String} viewMode default empty, (months|years)
22357  * @cfg {String} minViewMode default empty, (months|years)
22358  * @cfg {Number} startDate default -Infinity
22359  * @cfg {Number} endDate default Infinity
22360  * @cfg {Boolean} todayHighlight default false
22361  * @cfg {Boolean} todayBtn default false
22362  * @cfg {Boolean} calendarWeeks default false
22363  * @cfg {Object} daysOfWeekDisabled default empty
22364  * @cfg {Boolean} singleMode default false (true | false)
22365  * 
22366  * @cfg {Boolean} keyboardNavigation default true
22367  * @cfg {String} language default en
22368  * 
22369  * @constructor
22370  * Create a new DateField
22371  * @param {Object} config The config object
22372  */
22373
22374 Roo.bootstrap.DateField = function(config){
22375     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22376      this.addEvents({
22377             /**
22378              * @event show
22379              * Fires when this field show.
22380              * @param {Roo.bootstrap.DateField} this
22381              * @param {Mixed} date The date value
22382              */
22383             show : true,
22384             /**
22385              * @event show
22386              * Fires when this field hide.
22387              * @param {Roo.bootstrap.DateField} this
22388              * @param {Mixed} date The date value
22389              */
22390             hide : true,
22391             /**
22392              * @event select
22393              * Fires when select a date.
22394              * @param {Roo.bootstrap.DateField} this
22395              * @param {Mixed} date The date value
22396              */
22397             select : true,
22398             /**
22399              * @event beforeselect
22400              * Fires when before select a date.
22401              * @param {Roo.bootstrap.DateField} this
22402              * @param {Mixed} date The date value
22403              */
22404             beforeselect : true
22405         });
22406 };
22407
22408 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22409     
22410     /**
22411      * @cfg {String} format
22412      * The default date format string which can be overriden for localization support.  The format must be
22413      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22414      */
22415     format : "m/d/y",
22416     /**
22417      * @cfg {String} altFormats
22418      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22419      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22420      */
22421     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22422     
22423     weekStart : 0,
22424     
22425     viewMode : '',
22426     
22427     minViewMode : '',
22428     
22429     todayHighlight : false,
22430     
22431     todayBtn: false,
22432     
22433     language: 'en',
22434     
22435     keyboardNavigation: true,
22436     
22437     calendarWeeks: false,
22438     
22439     startDate: -Infinity,
22440     
22441     endDate: Infinity,
22442     
22443     daysOfWeekDisabled: [],
22444     
22445     _events: [],
22446     
22447     singleMode : false,
22448     
22449     UTCDate: function()
22450     {
22451         return new Date(Date.UTC.apply(Date, arguments));
22452     },
22453     
22454     UTCToday: function()
22455     {
22456         var today = new Date();
22457         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22458     },
22459     
22460     getDate: function() {
22461             var d = this.getUTCDate();
22462             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22463     },
22464     
22465     getUTCDate: function() {
22466             return this.date;
22467     },
22468     
22469     setDate: function(d) {
22470             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22471     },
22472     
22473     setUTCDate: function(d) {
22474             this.date = d;
22475             this.setValue(this.formatDate(this.date));
22476     },
22477         
22478     onRender: function(ct, position)
22479     {
22480         
22481         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22482         
22483         this.language = this.language || 'en';
22484         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22485         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22486         
22487         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22488         this.format = this.format || 'm/d/y';
22489         this.isInline = false;
22490         this.isInput = true;
22491         this.component = this.el.select('.add-on', true).first() || false;
22492         this.component = (this.component && this.component.length === 0) ? false : this.component;
22493         this.hasInput = this.component && this.inputEl().length;
22494         
22495         if (typeof(this.minViewMode === 'string')) {
22496             switch (this.minViewMode) {
22497                 case 'months':
22498                     this.minViewMode = 1;
22499                     break;
22500                 case 'years':
22501                     this.minViewMode = 2;
22502                     break;
22503                 default:
22504                     this.minViewMode = 0;
22505                     break;
22506             }
22507         }
22508         
22509         if (typeof(this.viewMode === 'string')) {
22510             switch (this.viewMode) {
22511                 case 'months':
22512                     this.viewMode = 1;
22513                     break;
22514                 case 'years':
22515                     this.viewMode = 2;
22516                     break;
22517                 default:
22518                     this.viewMode = 0;
22519                     break;
22520             }
22521         }
22522                 
22523         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22524         
22525 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22526         
22527         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22528         
22529         this.picker().on('mousedown', this.onMousedown, this);
22530         this.picker().on('click', this.onClick, this);
22531         
22532         this.picker().addClass('datepicker-dropdown');
22533         
22534         this.startViewMode = this.viewMode;
22535         
22536         if(this.singleMode){
22537             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22538                 v.setVisibilityMode(Roo.Element.DISPLAY);
22539                 v.hide();
22540             });
22541             
22542             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22543                 v.setStyle('width', '189px');
22544             });
22545         }
22546         
22547         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22548             if(!this.calendarWeeks){
22549                 v.remove();
22550                 return;
22551             }
22552             
22553             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22554             v.attr('colspan', function(i, val){
22555                 return parseInt(val) + 1;
22556             });
22557         });
22558                         
22559         
22560         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22561         
22562         this.setStartDate(this.startDate);
22563         this.setEndDate(this.endDate);
22564         
22565         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22566         
22567         this.fillDow();
22568         this.fillMonths();
22569         this.update();
22570         this.showMode();
22571         
22572         if(this.isInline) {
22573             this.showPopup();
22574         }
22575     },
22576     
22577     picker : function()
22578     {
22579         return this.pickerEl;
22580 //        return this.el.select('.datepicker', true).first();
22581     },
22582     
22583     fillDow: function()
22584     {
22585         var dowCnt = this.weekStart;
22586         
22587         var dow = {
22588             tag: 'tr',
22589             cn: [
22590                 
22591             ]
22592         };
22593         
22594         if(this.calendarWeeks){
22595             dow.cn.push({
22596                 tag: 'th',
22597                 cls: 'cw',
22598                 html: '&nbsp;'
22599             })
22600         }
22601         
22602         while (dowCnt < this.weekStart + 7) {
22603             dow.cn.push({
22604                 tag: 'th',
22605                 cls: 'dow',
22606                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22607             });
22608         }
22609         
22610         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22611     },
22612     
22613     fillMonths: function()
22614     {    
22615         var i = 0;
22616         var months = this.picker().select('>.datepicker-months td', true).first();
22617         
22618         months.dom.innerHTML = '';
22619         
22620         while (i < 12) {
22621             var month = {
22622                 tag: 'span',
22623                 cls: 'month',
22624                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22625             };
22626             
22627             months.createChild(month);
22628         }
22629         
22630     },
22631     
22632     update: function()
22633     {
22634         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;
22635         
22636         if (this.date < this.startDate) {
22637             this.viewDate = new Date(this.startDate);
22638         } else if (this.date > this.endDate) {
22639             this.viewDate = new Date(this.endDate);
22640         } else {
22641             this.viewDate = new Date(this.date);
22642         }
22643         
22644         this.fill();
22645     },
22646     
22647     fill: function() 
22648     {
22649         var d = new Date(this.viewDate),
22650                 year = d.getUTCFullYear(),
22651                 month = d.getUTCMonth(),
22652                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22653                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22654                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22655                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22656                 currentDate = this.date && this.date.valueOf(),
22657                 today = this.UTCToday();
22658         
22659         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22660         
22661 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22662         
22663 //        this.picker.select('>tfoot th.today').
22664 //                                              .text(dates[this.language].today)
22665 //                                              .toggle(this.todayBtn !== false);
22666     
22667         this.updateNavArrows();
22668         this.fillMonths();
22669                                                 
22670         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22671         
22672         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22673          
22674         prevMonth.setUTCDate(day);
22675         
22676         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22677         
22678         var nextMonth = new Date(prevMonth);
22679         
22680         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22681         
22682         nextMonth = nextMonth.valueOf();
22683         
22684         var fillMonths = false;
22685         
22686         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22687         
22688         while(prevMonth.valueOf() <= nextMonth) {
22689             var clsName = '';
22690             
22691             if (prevMonth.getUTCDay() === this.weekStart) {
22692                 if(fillMonths){
22693                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22694                 }
22695                     
22696                 fillMonths = {
22697                     tag: 'tr',
22698                     cn: []
22699                 };
22700                 
22701                 if(this.calendarWeeks){
22702                     // ISO 8601: First week contains first thursday.
22703                     // ISO also states week starts on Monday, but we can be more abstract here.
22704                     var
22705                     // Start of current week: based on weekstart/current date
22706                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22707                     // Thursday of this week
22708                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22709                     // First Thursday of year, year from thursday
22710                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22711                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22712                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22713                     
22714                     fillMonths.cn.push({
22715                         tag: 'td',
22716                         cls: 'cw',
22717                         html: calWeek
22718                     });
22719                 }
22720             }
22721             
22722             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22723                 clsName += ' old';
22724             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22725                 clsName += ' new';
22726             }
22727             if (this.todayHighlight &&
22728                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22729                 prevMonth.getUTCMonth() == today.getMonth() &&
22730                 prevMonth.getUTCDate() == today.getDate()) {
22731                 clsName += ' today';
22732             }
22733             
22734             if (currentDate && prevMonth.valueOf() === currentDate) {
22735                 clsName += ' active';
22736             }
22737             
22738             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22739                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22740                     clsName += ' disabled';
22741             }
22742             
22743             fillMonths.cn.push({
22744                 tag: 'td',
22745                 cls: 'day ' + clsName,
22746                 html: prevMonth.getDate()
22747             });
22748             
22749             prevMonth.setDate(prevMonth.getDate()+1);
22750         }
22751           
22752         var currentYear = this.date && this.date.getUTCFullYear();
22753         var currentMonth = this.date && this.date.getUTCMonth();
22754         
22755         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22756         
22757         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22758             v.removeClass('active');
22759             
22760             if(currentYear === year && k === currentMonth){
22761                 v.addClass('active');
22762             }
22763             
22764             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22765                 v.addClass('disabled');
22766             }
22767             
22768         });
22769         
22770         
22771         year = parseInt(year/10, 10) * 10;
22772         
22773         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22774         
22775         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22776         
22777         year -= 1;
22778         for (var i = -1; i < 11; i++) {
22779             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22780                 tag: 'span',
22781                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22782                 html: year
22783             });
22784             
22785             year += 1;
22786         }
22787     },
22788     
22789     showMode: function(dir) 
22790     {
22791         if (dir) {
22792             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22793         }
22794         
22795         Roo.each(this.picker().select('>div',true).elements, function(v){
22796             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22797             v.hide();
22798         });
22799         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22800     },
22801     
22802     place: function()
22803     {
22804         if(this.isInline) {
22805             return;
22806         }
22807         
22808         this.picker().removeClass(['bottom', 'top']);
22809         
22810         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22811             /*
22812              * place to the top of element!
22813              *
22814              */
22815             
22816             this.picker().addClass('top');
22817             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22818             
22819             return;
22820         }
22821         
22822         this.picker().addClass('bottom');
22823         
22824         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22825     },
22826     
22827     parseDate : function(value)
22828     {
22829         if(!value || value instanceof Date){
22830             return value;
22831         }
22832         var v = Date.parseDate(value, this.format);
22833         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22834             v = Date.parseDate(value, 'Y-m-d');
22835         }
22836         if(!v && this.altFormats){
22837             if(!this.altFormatsArray){
22838                 this.altFormatsArray = this.altFormats.split("|");
22839             }
22840             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22841                 v = Date.parseDate(value, this.altFormatsArray[i]);
22842             }
22843         }
22844         return v;
22845     },
22846     
22847     formatDate : function(date, fmt)
22848     {   
22849         return (!date || !(date instanceof Date)) ?
22850         date : date.dateFormat(fmt || this.format);
22851     },
22852     
22853     onFocus : function()
22854     {
22855         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22856         this.showPopup();
22857     },
22858     
22859     onBlur : function()
22860     {
22861         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22862         
22863         var d = this.inputEl().getValue();
22864         
22865         this.setValue(d);
22866                 
22867         this.hidePopup();
22868     },
22869     
22870     showPopup : function()
22871     {
22872         this.picker().show();
22873         this.update();
22874         this.place();
22875         
22876         this.fireEvent('showpopup', this, this.date);
22877     },
22878     
22879     hidePopup : function()
22880     {
22881         if(this.isInline) {
22882             return;
22883         }
22884         this.picker().hide();
22885         this.viewMode = this.startViewMode;
22886         this.showMode();
22887         
22888         this.fireEvent('hidepopup', this, this.date);
22889         
22890     },
22891     
22892     onMousedown: function(e)
22893     {
22894         e.stopPropagation();
22895         e.preventDefault();
22896     },
22897     
22898     keyup: function(e)
22899     {
22900         Roo.bootstrap.DateField.superclass.keyup.call(this);
22901         this.update();
22902     },
22903
22904     setValue: function(v)
22905     {
22906         if(this.fireEvent('beforeselect', this, v) !== false){
22907             var d = new Date(this.parseDate(v) ).clearTime();
22908         
22909             if(isNaN(d.getTime())){
22910                 this.date = this.viewDate = '';
22911                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22912                 return;
22913             }
22914
22915             v = this.formatDate(d);
22916
22917             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22918
22919             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22920
22921             this.update();
22922
22923             this.fireEvent('select', this, this.date);
22924         }
22925     },
22926     
22927     getValue: function()
22928     {
22929         return this.formatDate(this.date);
22930     },
22931     
22932     fireKey: function(e)
22933     {
22934         if (!this.picker().isVisible()){
22935             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22936                 this.showPopup();
22937             }
22938             return;
22939         }
22940         
22941         var dateChanged = false,
22942         dir, day, month,
22943         newDate, newViewDate;
22944         
22945         switch(e.keyCode){
22946             case 27: // escape
22947                 this.hidePopup();
22948                 e.preventDefault();
22949                 break;
22950             case 37: // left
22951             case 39: // right
22952                 if (!this.keyboardNavigation) {
22953                     break;
22954                 }
22955                 dir = e.keyCode == 37 ? -1 : 1;
22956                 
22957                 if (e.ctrlKey){
22958                     newDate = this.moveYear(this.date, dir);
22959                     newViewDate = this.moveYear(this.viewDate, dir);
22960                 } else if (e.shiftKey){
22961                     newDate = this.moveMonth(this.date, dir);
22962                     newViewDate = this.moveMonth(this.viewDate, dir);
22963                 } else {
22964                     newDate = new Date(this.date);
22965                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22966                     newViewDate = new Date(this.viewDate);
22967                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22968                 }
22969                 if (this.dateWithinRange(newDate)){
22970                     this.date = newDate;
22971                     this.viewDate = newViewDate;
22972                     this.setValue(this.formatDate(this.date));
22973 //                    this.update();
22974                     e.preventDefault();
22975                     dateChanged = true;
22976                 }
22977                 break;
22978             case 38: // up
22979             case 40: // down
22980                 if (!this.keyboardNavigation) {
22981                     break;
22982                 }
22983                 dir = e.keyCode == 38 ? -1 : 1;
22984                 if (e.ctrlKey){
22985                     newDate = this.moveYear(this.date, dir);
22986                     newViewDate = this.moveYear(this.viewDate, dir);
22987                 } else if (e.shiftKey){
22988                     newDate = this.moveMonth(this.date, dir);
22989                     newViewDate = this.moveMonth(this.viewDate, dir);
22990                 } else {
22991                     newDate = new Date(this.date);
22992                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22993                     newViewDate = new Date(this.viewDate);
22994                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22995                 }
22996                 if (this.dateWithinRange(newDate)){
22997                     this.date = newDate;
22998                     this.viewDate = newViewDate;
22999                     this.setValue(this.formatDate(this.date));
23000 //                    this.update();
23001                     e.preventDefault();
23002                     dateChanged = true;
23003                 }
23004                 break;
23005             case 13: // enter
23006                 this.setValue(this.formatDate(this.date));
23007                 this.hidePopup();
23008                 e.preventDefault();
23009                 break;
23010             case 9: // tab
23011                 this.setValue(this.formatDate(this.date));
23012                 this.hidePopup();
23013                 break;
23014             case 16: // shift
23015             case 17: // ctrl
23016             case 18: // alt
23017                 break;
23018             default :
23019                 this.hidePopup();
23020                 
23021         }
23022     },
23023     
23024     
23025     onClick: function(e) 
23026     {
23027         e.stopPropagation();
23028         e.preventDefault();
23029         
23030         var target = e.getTarget();
23031         
23032         if(target.nodeName.toLowerCase() === 'i'){
23033             target = Roo.get(target).dom.parentNode;
23034         }
23035         
23036         var nodeName = target.nodeName;
23037         var className = target.className;
23038         var html = target.innerHTML;
23039         //Roo.log(nodeName);
23040         
23041         switch(nodeName.toLowerCase()) {
23042             case 'th':
23043                 switch(className) {
23044                     case 'switch':
23045                         this.showMode(1);
23046                         break;
23047                     case 'prev':
23048                     case 'next':
23049                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23050                         switch(this.viewMode){
23051                                 case 0:
23052                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23053                                         break;
23054                                 case 1:
23055                                 case 2:
23056                                         this.viewDate = this.moveYear(this.viewDate, dir);
23057                                         break;
23058                         }
23059                         this.fill();
23060                         break;
23061                     case 'today':
23062                         var date = new Date();
23063                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23064 //                        this.fill()
23065                         this.setValue(this.formatDate(this.date));
23066                         
23067                         this.hidePopup();
23068                         break;
23069                 }
23070                 break;
23071             case 'span':
23072                 if (className.indexOf('disabled') < 0) {
23073                 if (!this.viewDate) {
23074                     this.viewDate = new Date();
23075                 }
23076                 this.viewDate.setUTCDate(1);
23077                     if (className.indexOf('month') > -1) {
23078                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23079                     } else {
23080                         var year = parseInt(html, 10) || 0;
23081                         this.viewDate.setUTCFullYear(year);
23082                         
23083                     }
23084                     
23085                     if(this.singleMode){
23086                         this.setValue(this.formatDate(this.viewDate));
23087                         this.hidePopup();
23088                         return;
23089                     }
23090                     
23091                     this.showMode(-1);
23092                     this.fill();
23093                 }
23094                 break;
23095                 
23096             case 'td':
23097                 //Roo.log(className);
23098                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23099                     var day = parseInt(html, 10) || 1;
23100                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23101                         month = (this.viewDate || new Date()).getUTCMonth();
23102
23103                     if (className.indexOf('old') > -1) {
23104                         if(month === 0 ){
23105                             month = 11;
23106                             year -= 1;
23107                         }else{
23108                             month -= 1;
23109                         }
23110                     } else if (className.indexOf('new') > -1) {
23111                         if (month == 11) {
23112                             month = 0;
23113                             year += 1;
23114                         } else {
23115                             month += 1;
23116                         }
23117                     }
23118                     //Roo.log([year,month,day]);
23119                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23120                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23121 //                    this.fill();
23122                     //Roo.log(this.formatDate(this.date));
23123                     this.setValue(this.formatDate(this.date));
23124                     this.hidePopup();
23125                 }
23126                 break;
23127         }
23128     },
23129     
23130     setStartDate: function(startDate)
23131     {
23132         this.startDate = startDate || -Infinity;
23133         if (this.startDate !== -Infinity) {
23134             this.startDate = this.parseDate(this.startDate);
23135         }
23136         this.update();
23137         this.updateNavArrows();
23138     },
23139
23140     setEndDate: function(endDate)
23141     {
23142         this.endDate = endDate || Infinity;
23143         if (this.endDate !== Infinity) {
23144             this.endDate = this.parseDate(this.endDate);
23145         }
23146         this.update();
23147         this.updateNavArrows();
23148     },
23149     
23150     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23151     {
23152         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23153         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23154             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23155         }
23156         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23157             return parseInt(d, 10);
23158         });
23159         this.update();
23160         this.updateNavArrows();
23161     },
23162     
23163     updateNavArrows: function() 
23164     {
23165         if(this.singleMode){
23166             return;
23167         }
23168         
23169         var d = new Date(this.viewDate),
23170         year = d.getUTCFullYear(),
23171         month = d.getUTCMonth();
23172         
23173         Roo.each(this.picker().select('.prev', true).elements, function(v){
23174             v.show();
23175             switch (this.viewMode) {
23176                 case 0:
23177
23178                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23179                         v.hide();
23180                     }
23181                     break;
23182                 case 1:
23183                 case 2:
23184                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23185                         v.hide();
23186                     }
23187                     break;
23188             }
23189         });
23190         
23191         Roo.each(this.picker().select('.next', true).elements, function(v){
23192             v.show();
23193             switch (this.viewMode) {
23194                 case 0:
23195
23196                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23197                         v.hide();
23198                     }
23199                     break;
23200                 case 1:
23201                 case 2:
23202                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23203                         v.hide();
23204                     }
23205                     break;
23206             }
23207         })
23208     },
23209     
23210     moveMonth: function(date, dir)
23211     {
23212         if (!dir) {
23213             return date;
23214         }
23215         var new_date = new Date(date.valueOf()),
23216         day = new_date.getUTCDate(),
23217         month = new_date.getUTCMonth(),
23218         mag = Math.abs(dir),
23219         new_month, test;
23220         dir = dir > 0 ? 1 : -1;
23221         if (mag == 1){
23222             test = dir == -1
23223             // If going back one month, make sure month is not current month
23224             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23225             ? function(){
23226                 return new_date.getUTCMonth() == month;
23227             }
23228             // If going forward one month, make sure month is as expected
23229             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23230             : function(){
23231                 return new_date.getUTCMonth() != new_month;
23232             };
23233             new_month = month + dir;
23234             new_date.setUTCMonth(new_month);
23235             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23236             if (new_month < 0 || new_month > 11) {
23237                 new_month = (new_month + 12) % 12;
23238             }
23239         } else {
23240             // For magnitudes >1, move one month at a time...
23241             for (var i=0; i<mag; i++) {
23242                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23243                 new_date = this.moveMonth(new_date, dir);
23244             }
23245             // ...then reset the day, keeping it in the new month
23246             new_month = new_date.getUTCMonth();
23247             new_date.setUTCDate(day);
23248             test = function(){
23249                 return new_month != new_date.getUTCMonth();
23250             };
23251         }
23252         // Common date-resetting loop -- if date is beyond end of month, make it
23253         // end of month
23254         while (test()){
23255             new_date.setUTCDate(--day);
23256             new_date.setUTCMonth(new_month);
23257         }
23258         return new_date;
23259     },
23260
23261     moveYear: function(date, dir)
23262     {
23263         return this.moveMonth(date, dir*12);
23264     },
23265
23266     dateWithinRange: function(date)
23267     {
23268         return date >= this.startDate && date <= this.endDate;
23269     },
23270
23271     
23272     remove: function() 
23273     {
23274         this.picker().remove();
23275     },
23276     
23277     validateValue : function(value)
23278     {
23279         if(this.getVisibilityEl().hasClass('hidden')){
23280             return true;
23281         }
23282         
23283         if(value.length < 1)  {
23284             if(this.allowBlank){
23285                 return true;
23286             }
23287             return false;
23288         }
23289         
23290         if(value.length < this.minLength){
23291             return false;
23292         }
23293         if(value.length > this.maxLength){
23294             return false;
23295         }
23296         if(this.vtype){
23297             var vt = Roo.form.VTypes;
23298             if(!vt[this.vtype](value, this)){
23299                 return false;
23300             }
23301         }
23302         if(typeof this.validator == "function"){
23303             var msg = this.validator(value);
23304             if(msg !== true){
23305                 return false;
23306             }
23307         }
23308         
23309         if(this.regex && !this.regex.test(value)){
23310             return false;
23311         }
23312         
23313         if(typeof(this.parseDate(value)) == 'undefined'){
23314             return false;
23315         }
23316         
23317         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23318             return false;
23319         }      
23320         
23321         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23322             return false;
23323         } 
23324         
23325         
23326         return true;
23327     },
23328     
23329     reset : function()
23330     {
23331         this.date = this.viewDate = '';
23332         
23333         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23334     }
23335    
23336 });
23337
23338 Roo.apply(Roo.bootstrap.DateField,  {
23339     
23340     head : {
23341         tag: 'thead',
23342         cn: [
23343         {
23344             tag: 'tr',
23345             cn: [
23346             {
23347                 tag: 'th',
23348                 cls: 'prev',
23349                 html: '<i class="fa fa-arrow-left"/>'
23350             },
23351             {
23352                 tag: 'th',
23353                 cls: 'switch',
23354                 colspan: '5'
23355             },
23356             {
23357                 tag: 'th',
23358                 cls: 'next',
23359                 html: '<i class="fa fa-arrow-right"/>'
23360             }
23361
23362             ]
23363         }
23364         ]
23365     },
23366     
23367     content : {
23368         tag: 'tbody',
23369         cn: [
23370         {
23371             tag: 'tr',
23372             cn: [
23373             {
23374                 tag: 'td',
23375                 colspan: '7'
23376             }
23377             ]
23378         }
23379         ]
23380     },
23381     
23382     footer : {
23383         tag: 'tfoot',
23384         cn: [
23385         {
23386             tag: 'tr',
23387             cn: [
23388             {
23389                 tag: 'th',
23390                 colspan: '7',
23391                 cls: 'today'
23392             }
23393                     
23394             ]
23395         }
23396         ]
23397     },
23398     
23399     dates:{
23400         en: {
23401             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23402             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23403             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23404             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23405             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23406             today: "Today"
23407         }
23408     },
23409     
23410     modes: [
23411     {
23412         clsName: 'days',
23413         navFnc: 'Month',
23414         navStep: 1
23415     },
23416     {
23417         clsName: 'months',
23418         navFnc: 'FullYear',
23419         navStep: 1
23420     },
23421     {
23422         clsName: 'years',
23423         navFnc: 'FullYear',
23424         navStep: 10
23425     }]
23426 });
23427
23428 Roo.apply(Roo.bootstrap.DateField,  {
23429   
23430     template : {
23431         tag: 'div',
23432         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23433         cn: [
23434         {
23435             tag: 'div',
23436             cls: 'datepicker-days',
23437             cn: [
23438             {
23439                 tag: 'table',
23440                 cls: 'table-condensed',
23441                 cn:[
23442                 Roo.bootstrap.DateField.head,
23443                 {
23444                     tag: 'tbody'
23445                 },
23446                 Roo.bootstrap.DateField.footer
23447                 ]
23448             }
23449             ]
23450         },
23451         {
23452             tag: 'div',
23453             cls: 'datepicker-months',
23454             cn: [
23455             {
23456                 tag: 'table',
23457                 cls: 'table-condensed',
23458                 cn:[
23459                 Roo.bootstrap.DateField.head,
23460                 Roo.bootstrap.DateField.content,
23461                 Roo.bootstrap.DateField.footer
23462                 ]
23463             }
23464             ]
23465         },
23466         {
23467             tag: 'div',
23468             cls: 'datepicker-years',
23469             cn: [
23470             {
23471                 tag: 'table',
23472                 cls: 'table-condensed',
23473                 cn:[
23474                 Roo.bootstrap.DateField.head,
23475                 Roo.bootstrap.DateField.content,
23476                 Roo.bootstrap.DateField.footer
23477                 ]
23478             }
23479             ]
23480         }
23481         ]
23482     }
23483 });
23484
23485  
23486
23487  /*
23488  * - LGPL
23489  *
23490  * TimeField
23491  * 
23492  */
23493
23494 /**
23495  * @class Roo.bootstrap.TimeField
23496  * @extends Roo.bootstrap.Input
23497  * Bootstrap DateField class
23498  * 
23499  * 
23500  * @constructor
23501  * Create a new TimeField
23502  * @param {Object} config The config object
23503  */
23504
23505 Roo.bootstrap.TimeField = function(config){
23506     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23507     this.addEvents({
23508             /**
23509              * @event show
23510              * Fires when this field show.
23511              * @param {Roo.bootstrap.DateField} thisthis
23512              * @param {Mixed} date The date value
23513              */
23514             show : true,
23515             /**
23516              * @event show
23517              * Fires when this field hide.
23518              * @param {Roo.bootstrap.DateField} this
23519              * @param {Mixed} date The date value
23520              */
23521             hide : true,
23522             /**
23523              * @event select
23524              * Fires when select a date.
23525              * @param {Roo.bootstrap.DateField} this
23526              * @param {Mixed} date The date value
23527              */
23528             select : true
23529         });
23530 };
23531
23532 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23533     
23534     /**
23535      * @cfg {String} format
23536      * The default time format string which can be overriden for localization support.  The format must be
23537      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23538      */
23539     format : "H:i",
23540
23541     getAutoCreate : function()
23542     {
23543         this.after = '<i class="fa far fa-clock"></i>';
23544         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23545         
23546          
23547     },
23548     onRender: function(ct, position)
23549     {
23550         
23551         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23552                 
23553         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23554         
23555         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23556         
23557         this.pop = this.picker().select('>.datepicker-time',true).first();
23558         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23559         
23560         this.picker().on('mousedown', this.onMousedown, this);
23561         this.picker().on('click', this.onClick, this);
23562         
23563         this.picker().addClass('datepicker-dropdown');
23564     
23565         this.fillTime();
23566         this.update();
23567             
23568         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23569         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23570         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23571         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23572         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23573         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23574
23575     },
23576     
23577     fireKey: function(e){
23578         if (!this.picker().isVisible()){
23579             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23580                 this.show();
23581             }
23582             return;
23583         }
23584
23585         e.preventDefault();
23586         
23587         switch(e.keyCode){
23588             case 27: // escape
23589                 this.hide();
23590                 break;
23591             case 37: // left
23592             case 39: // right
23593                 this.onTogglePeriod();
23594                 break;
23595             case 38: // up
23596                 this.onIncrementMinutes();
23597                 break;
23598             case 40: // down
23599                 this.onDecrementMinutes();
23600                 break;
23601             case 13: // enter
23602             case 9: // tab
23603                 this.setTime();
23604                 break;
23605         }
23606     },
23607     
23608     onClick: function(e) {
23609         e.stopPropagation();
23610         e.preventDefault();
23611     },
23612     
23613     picker : function()
23614     {
23615         return this.pickerEl;
23616     },
23617     
23618     fillTime: function()
23619     {    
23620         var time = this.pop.select('tbody', true).first();
23621         
23622         time.dom.innerHTML = '';
23623         
23624         time.createChild({
23625             tag: 'tr',
23626             cn: [
23627                 {
23628                     tag: 'td',
23629                     cn: [
23630                         {
23631                             tag: 'a',
23632                             href: '#',
23633                             cls: 'btn',
23634                             cn: [
23635                                 {
23636                                     tag: 'i',
23637                                     cls: 'hours-up fa fas fa-chevron-up'
23638                                 }
23639                             ]
23640                         } 
23641                     ]
23642                 },
23643                 {
23644                     tag: 'td',
23645                     cls: 'separator'
23646                 },
23647                 {
23648                     tag: 'td',
23649                     cn: [
23650                         {
23651                             tag: 'a',
23652                             href: '#',
23653                             cls: 'btn',
23654                             cn: [
23655                                 {
23656                                     tag: 'i',
23657                                     cls: 'minutes-up fa fas fa-chevron-up'
23658                                 }
23659                             ]
23660                         }
23661                     ]
23662                 },
23663                 {
23664                     tag: 'td',
23665                     cls: 'separator'
23666                 }
23667             ]
23668         });
23669         
23670         time.createChild({
23671             tag: 'tr',
23672             cn: [
23673                 {
23674                     tag: 'td',
23675                     cn: [
23676                         {
23677                             tag: 'span',
23678                             cls: 'timepicker-hour',
23679                             html: '00'
23680                         }  
23681                     ]
23682                 },
23683                 {
23684                     tag: 'td',
23685                     cls: 'separator',
23686                     html: ':'
23687                 },
23688                 {
23689                     tag: 'td',
23690                     cn: [
23691                         {
23692                             tag: 'span',
23693                             cls: 'timepicker-minute',
23694                             html: '00'
23695                         }  
23696                     ]
23697                 },
23698                 {
23699                     tag: 'td',
23700                     cls: 'separator'
23701                 },
23702                 {
23703                     tag: 'td',
23704                     cn: [
23705                         {
23706                             tag: 'button',
23707                             type: 'button',
23708                             cls: 'btn btn-primary period',
23709                             html: 'AM'
23710                             
23711                         }
23712                     ]
23713                 }
23714             ]
23715         });
23716         
23717         time.createChild({
23718             tag: 'tr',
23719             cn: [
23720                 {
23721                     tag: 'td',
23722                     cn: [
23723                         {
23724                             tag: 'a',
23725                             href: '#',
23726                             cls: 'btn',
23727                             cn: [
23728                                 {
23729                                     tag: 'span',
23730                                     cls: 'hours-down fa fas fa-chevron-down'
23731                                 }
23732                             ]
23733                         }
23734                     ]
23735                 },
23736                 {
23737                     tag: 'td',
23738                     cls: 'separator'
23739                 },
23740                 {
23741                     tag: 'td',
23742                     cn: [
23743                         {
23744                             tag: 'a',
23745                             href: '#',
23746                             cls: 'btn',
23747                             cn: [
23748                                 {
23749                                     tag: 'span',
23750                                     cls: 'minutes-down fa fas fa-chevron-down'
23751                                 }
23752                             ]
23753                         }
23754                     ]
23755                 },
23756                 {
23757                     tag: 'td',
23758                     cls: 'separator'
23759                 }
23760             ]
23761         });
23762         
23763     },
23764     
23765     update: function()
23766     {
23767         
23768         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23769         
23770         this.fill();
23771     },
23772     
23773     fill: function() 
23774     {
23775         var hours = this.time.getHours();
23776         var minutes = this.time.getMinutes();
23777         var period = 'AM';
23778         
23779         if(hours > 11){
23780             period = 'PM';
23781         }
23782         
23783         if(hours == 0){
23784             hours = 12;
23785         }
23786         
23787         
23788         if(hours > 12){
23789             hours = hours - 12;
23790         }
23791         
23792         if(hours < 10){
23793             hours = '0' + hours;
23794         }
23795         
23796         if(minutes < 10){
23797             minutes = '0' + minutes;
23798         }
23799         
23800         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23801         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23802         this.pop.select('button', true).first().dom.innerHTML = period;
23803         
23804     },
23805     
23806     place: function()
23807     {   
23808         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23809         
23810         var cls = ['bottom'];
23811         
23812         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23813             cls.pop();
23814             cls.push('top');
23815         }
23816         
23817         cls.push('right');
23818         
23819         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23820             cls.pop();
23821             cls.push('left');
23822         }
23823         //this.picker().setXY(20000,20000);
23824         this.picker().addClass(cls.join('-'));
23825         
23826         var _this = this;
23827         
23828         Roo.each(cls, function(c){
23829             if(c == 'bottom'){
23830                 (function() {
23831                  //  
23832                 }).defer(200);
23833                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23834                 //_this.picker().setTop(_this.inputEl().getHeight());
23835                 return;
23836             }
23837             if(c == 'top'){
23838                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23839                 
23840                 //_this.picker().setTop(0 - _this.picker().getHeight());
23841                 return;
23842             }
23843             /*
23844             if(c == 'left'){
23845                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23846                 return;
23847             }
23848             if(c == 'right'){
23849                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23850                 return;
23851             }
23852             */
23853         });
23854         
23855     },
23856   
23857     onFocus : function()
23858     {
23859         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23860         this.show();
23861     },
23862     
23863     onBlur : function()
23864     {
23865         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23866         this.hide();
23867     },
23868     
23869     show : function()
23870     {
23871         this.picker().show();
23872         this.pop.show();
23873         this.update();
23874         this.place();
23875         
23876         this.fireEvent('show', this, this.date);
23877     },
23878     
23879     hide : function()
23880     {
23881         this.picker().hide();
23882         this.pop.hide();
23883         
23884         this.fireEvent('hide', this, this.date);
23885     },
23886     
23887     setTime : function()
23888     {
23889         this.hide();
23890         this.setValue(this.time.format(this.format));
23891         
23892         this.fireEvent('select', this, this.date);
23893         
23894         
23895     },
23896     
23897     onMousedown: function(e){
23898         e.stopPropagation();
23899         e.preventDefault();
23900     },
23901     
23902     onIncrementHours: function()
23903     {
23904         Roo.log('onIncrementHours');
23905         this.time = this.time.add(Date.HOUR, 1);
23906         this.update();
23907         
23908     },
23909     
23910     onDecrementHours: function()
23911     {
23912         Roo.log('onDecrementHours');
23913         this.time = this.time.add(Date.HOUR, -1);
23914         this.update();
23915     },
23916     
23917     onIncrementMinutes: function()
23918     {
23919         Roo.log('onIncrementMinutes');
23920         this.time = this.time.add(Date.MINUTE, 1);
23921         this.update();
23922     },
23923     
23924     onDecrementMinutes: function()
23925     {
23926         Roo.log('onDecrementMinutes');
23927         this.time = this.time.add(Date.MINUTE, -1);
23928         this.update();
23929     },
23930     
23931     onTogglePeriod: function()
23932     {
23933         Roo.log('onTogglePeriod');
23934         this.time = this.time.add(Date.HOUR, 12);
23935         this.update();
23936     }
23937     
23938    
23939 });
23940  
23941
23942 Roo.apply(Roo.bootstrap.TimeField,  {
23943   
23944     template : {
23945         tag: 'div',
23946         cls: 'datepicker dropdown-menu',
23947         cn: [
23948             {
23949                 tag: 'div',
23950                 cls: 'datepicker-time',
23951                 cn: [
23952                 {
23953                     tag: 'table',
23954                     cls: 'table-condensed',
23955                     cn:[
23956                         {
23957                             tag: 'tbody',
23958                             cn: [
23959                                 {
23960                                     tag: 'tr',
23961                                     cn: [
23962                                     {
23963                                         tag: 'td',
23964                                         colspan: '7'
23965                                     }
23966                                     ]
23967                                 }
23968                             ]
23969                         },
23970                         {
23971                             tag: 'tfoot',
23972                             cn: [
23973                                 {
23974                                     tag: 'tr',
23975                                     cn: [
23976                                     {
23977                                         tag: 'th',
23978                                         colspan: '7',
23979                                         cls: '',
23980                                         cn: [
23981                                             {
23982                                                 tag: 'button',
23983                                                 cls: 'btn btn-info ok',
23984                                                 html: 'OK'
23985                                             }
23986                                         ]
23987                                     }
23988                     
23989                                     ]
23990                                 }
23991                             ]
23992                         }
23993                     ]
23994                 }
23995                 ]
23996             }
23997         ]
23998     }
23999 });
24000
24001  
24002
24003  /*
24004  * - LGPL
24005  *
24006  * MonthField
24007  * 
24008  */
24009
24010 /**
24011  * @class Roo.bootstrap.MonthField
24012  * @extends Roo.bootstrap.Input
24013  * Bootstrap MonthField class
24014  * 
24015  * @cfg {String} language default en
24016  * 
24017  * @constructor
24018  * Create a new MonthField
24019  * @param {Object} config The config object
24020  */
24021
24022 Roo.bootstrap.MonthField = function(config){
24023     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
24024     
24025     this.addEvents({
24026         /**
24027          * @event show
24028          * Fires when this field show.
24029          * @param {Roo.bootstrap.MonthField} this
24030          * @param {Mixed} date The date value
24031          */
24032         show : true,
24033         /**
24034          * @event show
24035          * Fires when this field hide.
24036          * @param {Roo.bootstrap.MonthField} this
24037          * @param {Mixed} date The date value
24038          */
24039         hide : true,
24040         /**
24041          * @event select
24042          * Fires when select a date.
24043          * @param {Roo.bootstrap.MonthField} this
24044          * @param {String} oldvalue The old value
24045          * @param {String} newvalue The new value
24046          */
24047         select : true
24048     });
24049 };
24050
24051 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24052     
24053     onRender: function(ct, position)
24054     {
24055         
24056         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24057         
24058         this.language = this.language || 'en';
24059         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24060         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24061         
24062         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24063         this.isInline = false;
24064         this.isInput = true;
24065         this.component = this.el.select('.add-on', true).first() || false;
24066         this.component = (this.component && this.component.length === 0) ? false : this.component;
24067         this.hasInput = this.component && this.inputEL().length;
24068         
24069         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24070         
24071         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24072         
24073         this.picker().on('mousedown', this.onMousedown, this);
24074         this.picker().on('click', this.onClick, this);
24075         
24076         this.picker().addClass('datepicker-dropdown');
24077         
24078         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24079             v.setStyle('width', '189px');
24080         });
24081         
24082         this.fillMonths();
24083         
24084         this.update();
24085         
24086         if(this.isInline) {
24087             this.show();
24088         }
24089         
24090     },
24091     
24092     setValue: function(v, suppressEvent)
24093     {   
24094         var o = this.getValue();
24095         
24096         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24097         
24098         this.update();
24099
24100         if(suppressEvent !== true){
24101             this.fireEvent('select', this, o, v);
24102         }
24103         
24104     },
24105     
24106     getValue: function()
24107     {
24108         return this.value;
24109     },
24110     
24111     onClick: function(e) 
24112     {
24113         e.stopPropagation();
24114         e.preventDefault();
24115         
24116         var target = e.getTarget();
24117         
24118         if(target.nodeName.toLowerCase() === 'i'){
24119             target = Roo.get(target).dom.parentNode;
24120         }
24121         
24122         var nodeName = target.nodeName;
24123         var className = target.className;
24124         var html = target.innerHTML;
24125         
24126         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24127             return;
24128         }
24129         
24130         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24131         
24132         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24133         
24134         this.hide();
24135                         
24136     },
24137     
24138     picker : function()
24139     {
24140         return this.pickerEl;
24141     },
24142     
24143     fillMonths: function()
24144     {    
24145         var i = 0;
24146         var months = this.picker().select('>.datepicker-months td', true).first();
24147         
24148         months.dom.innerHTML = '';
24149         
24150         while (i < 12) {
24151             var month = {
24152                 tag: 'span',
24153                 cls: 'month',
24154                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24155             };
24156             
24157             months.createChild(month);
24158         }
24159         
24160     },
24161     
24162     update: function()
24163     {
24164         var _this = this;
24165         
24166         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24167             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24168         }
24169         
24170         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24171             e.removeClass('active');
24172             
24173             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24174                 e.addClass('active');
24175             }
24176         })
24177     },
24178     
24179     place: function()
24180     {
24181         if(this.isInline) {
24182             return;
24183         }
24184         
24185         this.picker().removeClass(['bottom', 'top']);
24186         
24187         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24188             /*
24189              * place to the top of element!
24190              *
24191              */
24192             
24193             this.picker().addClass('top');
24194             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24195             
24196             return;
24197         }
24198         
24199         this.picker().addClass('bottom');
24200         
24201         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24202     },
24203     
24204     onFocus : function()
24205     {
24206         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24207         this.show();
24208     },
24209     
24210     onBlur : function()
24211     {
24212         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24213         
24214         var d = this.inputEl().getValue();
24215         
24216         this.setValue(d);
24217                 
24218         this.hide();
24219     },
24220     
24221     show : function()
24222     {
24223         this.picker().show();
24224         this.picker().select('>.datepicker-months', true).first().show();
24225         this.update();
24226         this.place();
24227         
24228         this.fireEvent('show', this, this.date);
24229     },
24230     
24231     hide : function()
24232     {
24233         if(this.isInline) {
24234             return;
24235         }
24236         this.picker().hide();
24237         this.fireEvent('hide', this, this.date);
24238         
24239     },
24240     
24241     onMousedown: function(e)
24242     {
24243         e.stopPropagation();
24244         e.preventDefault();
24245     },
24246     
24247     keyup: function(e)
24248     {
24249         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24250         this.update();
24251     },
24252
24253     fireKey: function(e)
24254     {
24255         if (!this.picker().isVisible()){
24256             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24257                 this.show();
24258             }
24259             return;
24260         }
24261         
24262         var dir;
24263         
24264         switch(e.keyCode){
24265             case 27: // escape
24266                 this.hide();
24267                 e.preventDefault();
24268                 break;
24269             case 37: // left
24270             case 39: // right
24271                 dir = e.keyCode == 37 ? -1 : 1;
24272                 
24273                 this.vIndex = this.vIndex + dir;
24274                 
24275                 if(this.vIndex < 0){
24276                     this.vIndex = 0;
24277                 }
24278                 
24279                 if(this.vIndex > 11){
24280                     this.vIndex = 11;
24281                 }
24282                 
24283                 if(isNaN(this.vIndex)){
24284                     this.vIndex = 0;
24285                 }
24286                 
24287                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24288                 
24289                 break;
24290             case 38: // up
24291             case 40: // down
24292                 
24293                 dir = e.keyCode == 38 ? -1 : 1;
24294                 
24295                 this.vIndex = this.vIndex + dir * 4;
24296                 
24297                 if(this.vIndex < 0){
24298                     this.vIndex = 0;
24299                 }
24300                 
24301                 if(this.vIndex > 11){
24302                     this.vIndex = 11;
24303                 }
24304                 
24305                 if(isNaN(this.vIndex)){
24306                     this.vIndex = 0;
24307                 }
24308                 
24309                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24310                 break;
24311                 
24312             case 13: // enter
24313                 
24314                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24315                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24316                 }
24317                 
24318                 this.hide();
24319                 e.preventDefault();
24320                 break;
24321             case 9: // tab
24322                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24323                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24324                 }
24325                 this.hide();
24326                 break;
24327             case 16: // shift
24328             case 17: // ctrl
24329             case 18: // alt
24330                 break;
24331             default :
24332                 this.hide();
24333                 
24334         }
24335     },
24336     
24337     remove: function() 
24338     {
24339         this.picker().remove();
24340     }
24341    
24342 });
24343
24344 Roo.apply(Roo.bootstrap.MonthField,  {
24345     
24346     content : {
24347         tag: 'tbody',
24348         cn: [
24349         {
24350             tag: 'tr',
24351             cn: [
24352             {
24353                 tag: 'td',
24354                 colspan: '7'
24355             }
24356             ]
24357         }
24358         ]
24359     },
24360     
24361     dates:{
24362         en: {
24363             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24364             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24365         }
24366     }
24367 });
24368
24369 Roo.apply(Roo.bootstrap.MonthField,  {
24370   
24371     template : {
24372         tag: 'div',
24373         cls: 'datepicker dropdown-menu roo-dynamic',
24374         cn: [
24375             {
24376                 tag: 'div',
24377                 cls: 'datepicker-months',
24378                 cn: [
24379                 {
24380                     tag: 'table',
24381                     cls: 'table-condensed',
24382                     cn:[
24383                         Roo.bootstrap.DateField.content
24384                     ]
24385                 }
24386                 ]
24387             }
24388         ]
24389     }
24390 });
24391
24392  
24393
24394  
24395  /*
24396  * - LGPL
24397  *
24398  * CheckBox
24399  * 
24400  */
24401
24402 /**
24403  * @class Roo.bootstrap.CheckBox
24404  * @extends Roo.bootstrap.Input
24405  * Bootstrap CheckBox class
24406  * 
24407  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24408  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24409  * @cfg {String} boxLabel The text that appears beside the checkbox
24410  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24411  * @cfg {Boolean} checked initnal the element
24412  * @cfg {Boolean} inline inline the element (default false)
24413  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24414  * @cfg {String} tooltip label tooltip
24415  * 
24416  * @constructor
24417  * Create a new CheckBox
24418  * @param {Object} config The config object
24419  */
24420
24421 Roo.bootstrap.CheckBox = function(config){
24422     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24423    
24424     this.addEvents({
24425         /**
24426         * @event check
24427         * Fires when the element is checked or unchecked.
24428         * @param {Roo.bootstrap.CheckBox} this This input
24429         * @param {Boolean} checked The new checked value
24430         */
24431        check : true,
24432        /**
24433         * @event click
24434         * Fires when the element is click.
24435         * @param {Roo.bootstrap.CheckBox} this This input
24436         */
24437        click : true
24438     });
24439     
24440 };
24441
24442 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24443   
24444     inputType: 'checkbox',
24445     inputValue: 1,
24446     valueOff: 0,
24447     boxLabel: false,
24448     checked: false,
24449     weight : false,
24450     inline: false,
24451     tooltip : '',
24452     
24453     // checkbox success does not make any sense really.. 
24454     invalidClass : "",
24455     validClass : "",
24456     
24457     
24458     getAutoCreate : function()
24459     {
24460         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24461         
24462         var id = Roo.id();
24463         
24464         var cfg = {};
24465         
24466         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24467         
24468         if(this.inline){
24469             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24470         }
24471         
24472         var input =  {
24473             tag: 'input',
24474             id : id,
24475             type : this.inputType,
24476             value : this.inputValue,
24477             cls : 'roo-' + this.inputType, //'form-box',
24478             placeholder : this.placeholder || ''
24479             
24480         };
24481         
24482         if(this.inputType != 'radio'){
24483             var hidden =  {
24484                 tag: 'input',
24485                 type : 'hidden',
24486                 cls : 'roo-hidden-value',
24487                 value : this.checked ? this.inputValue : this.valueOff
24488             };
24489         }
24490         
24491             
24492         if (this.weight) { // Validity check?
24493             cfg.cls += " " + this.inputType + "-" + this.weight;
24494         }
24495         
24496         if (this.disabled) {
24497             input.disabled=true;
24498         }
24499         
24500         if(this.checked){
24501             input.checked = this.checked;
24502         }
24503         
24504         if (this.name) {
24505             
24506             input.name = this.name;
24507             
24508             if(this.inputType != 'radio'){
24509                 hidden.name = this.name;
24510                 input.name = '_hidden_' + this.name;
24511             }
24512         }
24513         
24514         if (this.size) {
24515             input.cls += ' input-' + this.size;
24516         }
24517         
24518         var settings=this;
24519         
24520         ['xs','sm','md','lg'].map(function(size){
24521             if (settings[size]) {
24522                 cfg.cls += ' col-' + size + '-' + settings[size];
24523             }
24524         });
24525         
24526         var inputblock = input;
24527          
24528         if (this.before || this.after) {
24529             
24530             inputblock = {
24531                 cls : 'input-group',
24532                 cn :  [] 
24533             };
24534             
24535             if (this.before) {
24536                 inputblock.cn.push({
24537                     tag :'span',
24538                     cls : 'input-group-addon',
24539                     html : this.before
24540                 });
24541             }
24542             
24543             inputblock.cn.push(input);
24544             
24545             if(this.inputType != 'radio'){
24546                 inputblock.cn.push(hidden);
24547             }
24548             
24549             if (this.after) {
24550                 inputblock.cn.push({
24551                     tag :'span',
24552                     cls : 'input-group-addon',
24553                     html : this.after
24554                 });
24555             }
24556             
24557         }
24558         var boxLabelCfg = false;
24559         
24560         if(this.boxLabel){
24561            
24562             boxLabelCfg = {
24563                 tag: 'label',
24564                 //'for': id, // box label is handled by onclick - so no for...
24565                 cls: 'box-label',
24566                 html: this.boxLabel
24567             };
24568             if(this.tooltip){
24569                 boxLabelCfg.tooltip = this.tooltip;
24570             }
24571              
24572         }
24573         
24574         
24575         if (align ==='left' && this.fieldLabel.length) {
24576 //                Roo.log("left and has label");
24577             cfg.cn = [
24578                 {
24579                     tag: 'label',
24580                     'for' :  id,
24581                     cls : 'control-label',
24582                     html : this.fieldLabel
24583                 },
24584                 {
24585                     cls : "", 
24586                     cn: [
24587                         inputblock
24588                     ]
24589                 }
24590             ];
24591             
24592             if (boxLabelCfg) {
24593                 cfg.cn[1].cn.push(boxLabelCfg);
24594             }
24595             
24596             if(this.labelWidth > 12){
24597                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24598             }
24599             
24600             if(this.labelWidth < 13 && this.labelmd == 0){
24601                 this.labelmd = this.labelWidth;
24602             }
24603             
24604             if(this.labellg > 0){
24605                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24606                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24607             }
24608             
24609             if(this.labelmd > 0){
24610                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24611                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24612             }
24613             
24614             if(this.labelsm > 0){
24615                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24616                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24617             }
24618             
24619             if(this.labelxs > 0){
24620                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24621                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24622             }
24623             
24624         } else if ( this.fieldLabel.length) {
24625 //                Roo.log(" label");
24626                 cfg.cn = [
24627                    
24628                     {
24629                         tag: this.boxLabel ? 'span' : 'label',
24630                         'for': id,
24631                         cls: 'control-label box-input-label',
24632                         //cls : 'input-group-addon',
24633                         html : this.fieldLabel
24634                     },
24635                     
24636                     inputblock
24637                     
24638                 ];
24639                 if (boxLabelCfg) {
24640                     cfg.cn.push(boxLabelCfg);
24641                 }
24642
24643         } else {
24644             
24645 //                Roo.log(" no label && no align");
24646                 cfg.cn = [  inputblock ] ;
24647                 if (boxLabelCfg) {
24648                     cfg.cn.push(boxLabelCfg);
24649                 }
24650
24651                 
24652         }
24653         
24654        
24655         
24656         if(this.inputType != 'radio'){
24657             cfg.cn.push(hidden);
24658         }
24659         
24660         return cfg;
24661         
24662     },
24663     
24664     /**
24665      * return the real input element.
24666      */
24667     inputEl: function ()
24668     {
24669         return this.el.select('input.roo-' + this.inputType,true).first();
24670     },
24671     hiddenEl: function ()
24672     {
24673         return this.el.select('input.roo-hidden-value',true).first();
24674     },
24675     
24676     labelEl: function()
24677     {
24678         return this.el.select('label.control-label',true).first();
24679     },
24680     /* depricated... */
24681     
24682     label: function()
24683     {
24684         return this.labelEl();
24685     },
24686     
24687     boxLabelEl: function()
24688     {
24689         return this.el.select('label.box-label',true).first();
24690     },
24691     
24692     initEvents : function()
24693     {
24694 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24695         
24696         this.inputEl().on('click', this.onClick,  this);
24697         
24698         if (this.boxLabel) { 
24699             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24700         }
24701         
24702         this.startValue = this.getValue();
24703         
24704         if(this.groupId){
24705             Roo.bootstrap.CheckBox.register(this);
24706         }
24707     },
24708     
24709     onClick : function(e)
24710     {   
24711         if(this.fireEvent('click', this, e) !== false){
24712             this.setChecked(!this.checked);
24713         }
24714         
24715     },
24716     
24717     setChecked : function(state,suppressEvent)
24718     {
24719         this.startValue = this.getValue();
24720
24721         if(this.inputType == 'radio'){
24722             
24723             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24724                 e.dom.checked = false;
24725             });
24726             
24727             this.inputEl().dom.checked = true;
24728             
24729             this.inputEl().dom.value = this.inputValue;
24730             
24731             if(suppressEvent !== true){
24732                 this.fireEvent('check', this, true);
24733             }
24734             
24735             this.validate();
24736             
24737             return;
24738         }
24739         
24740         this.checked = state;
24741         
24742         this.inputEl().dom.checked = state;
24743         
24744         
24745         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24746         
24747         if(suppressEvent !== true){
24748             this.fireEvent('check', this, state);
24749         }
24750         
24751         this.validate();
24752     },
24753     
24754     getValue : function()
24755     {
24756         if(this.inputType == 'radio'){
24757             return this.getGroupValue();
24758         }
24759         
24760         return this.hiddenEl().dom.value;
24761         
24762     },
24763     
24764     getGroupValue : function()
24765     {
24766         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24767             return '';
24768         }
24769         
24770         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24771     },
24772     
24773     setValue : function(v,suppressEvent)
24774     {
24775         if(this.inputType == 'radio'){
24776             this.setGroupValue(v, suppressEvent);
24777             return;
24778         }
24779         
24780         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24781         
24782         this.validate();
24783     },
24784     
24785     setGroupValue : function(v, suppressEvent)
24786     {
24787         this.startValue = this.getValue();
24788         
24789         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24790             e.dom.checked = false;
24791             
24792             if(e.dom.value == v){
24793                 e.dom.checked = true;
24794             }
24795         });
24796         
24797         if(suppressEvent !== true){
24798             this.fireEvent('check', this, true);
24799         }
24800
24801         this.validate();
24802         
24803         return;
24804     },
24805     
24806     validate : function()
24807     {
24808         if(this.getVisibilityEl().hasClass('hidden')){
24809             return true;
24810         }
24811         
24812         if(
24813                 this.disabled || 
24814                 (this.inputType == 'radio' && this.validateRadio()) ||
24815                 (this.inputType == 'checkbox' && this.validateCheckbox())
24816         ){
24817             this.markValid();
24818             return true;
24819         }
24820         
24821         this.markInvalid();
24822         return false;
24823     },
24824     
24825     validateRadio : function()
24826     {
24827         if(this.getVisibilityEl().hasClass('hidden')){
24828             return true;
24829         }
24830         
24831         if(this.allowBlank){
24832             return true;
24833         }
24834         
24835         var valid = false;
24836         
24837         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24838             if(!e.dom.checked){
24839                 return;
24840             }
24841             
24842             valid = true;
24843             
24844             return false;
24845         });
24846         
24847         return valid;
24848     },
24849     
24850     validateCheckbox : function()
24851     {
24852         if(!this.groupId){
24853             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24854             //return (this.getValue() == this.inputValue) ? true : false;
24855         }
24856         
24857         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24858         
24859         if(!group){
24860             return false;
24861         }
24862         
24863         var r = false;
24864         
24865         for(var i in group){
24866             if(group[i].el.isVisible(true)){
24867                 r = false;
24868                 break;
24869             }
24870             
24871             r = true;
24872         }
24873         
24874         for(var i in group){
24875             if(r){
24876                 break;
24877             }
24878             
24879             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24880         }
24881         
24882         return r;
24883     },
24884     
24885     /**
24886      * Mark this field as valid
24887      */
24888     markValid : function()
24889     {
24890         var _this = this;
24891         
24892         this.fireEvent('valid', this);
24893         
24894         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24895         
24896         if(this.groupId){
24897             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24898         }
24899         
24900         if(label){
24901             label.markValid();
24902         }
24903
24904         if(this.inputType == 'radio'){
24905             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24906                 var fg = e.findParent('.form-group', false, true);
24907                 if (Roo.bootstrap.version == 3) {
24908                     fg.removeClass([_this.invalidClass, _this.validClass]);
24909                     fg.addClass(_this.validClass);
24910                 } else {
24911                     fg.removeClass(['is-valid', 'is-invalid']);
24912                     fg.addClass('is-valid');
24913                 }
24914             });
24915             
24916             return;
24917         }
24918
24919         if(!this.groupId){
24920             var fg = this.el.findParent('.form-group', false, true);
24921             if (Roo.bootstrap.version == 3) {
24922                 fg.removeClass([this.invalidClass, this.validClass]);
24923                 fg.addClass(this.validClass);
24924             } else {
24925                 fg.removeClass(['is-valid', 'is-invalid']);
24926                 fg.addClass('is-valid');
24927             }
24928             return;
24929         }
24930         
24931         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24932         
24933         if(!group){
24934             return;
24935         }
24936         
24937         for(var i in group){
24938             var fg = group[i].el.findParent('.form-group', false, true);
24939             if (Roo.bootstrap.version == 3) {
24940                 fg.removeClass([this.invalidClass, this.validClass]);
24941                 fg.addClass(this.validClass);
24942             } else {
24943                 fg.removeClass(['is-valid', 'is-invalid']);
24944                 fg.addClass('is-valid');
24945             }
24946         }
24947     },
24948     
24949      /**
24950      * Mark this field as invalid
24951      * @param {String} msg The validation message
24952      */
24953     markInvalid : function(msg)
24954     {
24955         if(this.allowBlank){
24956             return;
24957         }
24958         
24959         var _this = this;
24960         
24961         this.fireEvent('invalid', this, msg);
24962         
24963         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24964         
24965         if(this.groupId){
24966             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24967         }
24968         
24969         if(label){
24970             label.markInvalid();
24971         }
24972             
24973         if(this.inputType == 'radio'){
24974             
24975             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24976                 var fg = e.findParent('.form-group', false, true);
24977                 if (Roo.bootstrap.version == 3) {
24978                     fg.removeClass([_this.invalidClass, _this.validClass]);
24979                     fg.addClass(_this.invalidClass);
24980                 } else {
24981                     fg.removeClass(['is-invalid', 'is-valid']);
24982                     fg.addClass('is-invalid');
24983                 }
24984             });
24985             
24986             return;
24987         }
24988         
24989         if(!this.groupId){
24990             var fg = this.el.findParent('.form-group', false, true);
24991             if (Roo.bootstrap.version == 3) {
24992                 fg.removeClass([_this.invalidClass, _this.validClass]);
24993                 fg.addClass(_this.invalidClass);
24994             } else {
24995                 fg.removeClass(['is-invalid', 'is-valid']);
24996                 fg.addClass('is-invalid');
24997             }
24998             return;
24999         }
25000         
25001         var group = Roo.bootstrap.CheckBox.get(this.groupId);
25002         
25003         if(!group){
25004             return;
25005         }
25006         
25007         for(var i in group){
25008             var fg = group[i].el.findParent('.form-group', false, true);
25009             if (Roo.bootstrap.version == 3) {
25010                 fg.removeClass([_this.invalidClass, _this.validClass]);
25011                 fg.addClass(_this.invalidClass);
25012             } else {
25013                 fg.removeClass(['is-invalid', 'is-valid']);
25014                 fg.addClass('is-invalid');
25015             }
25016         }
25017         
25018     },
25019     
25020     clearInvalid : function()
25021     {
25022         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
25023         
25024         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25025         
25026         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25027         
25028         if (label && label.iconEl) {
25029             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25030             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25031         }
25032     },
25033     
25034     disable : function()
25035     {
25036         if(this.inputType != 'radio'){
25037             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25038             return;
25039         }
25040         
25041         var _this = this;
25042         
25043         if(this.rendered){
25044             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25045                 _this.getActionEl().addClass(this.disabledClass);
25046                 e.dom.disabled = true;
25047             });
25048         }
25049         
25050         this.disabled = true;
25051         this.fireEvent("disable", this);
25052         return this;
25053     },
25054
25055     enable : function()
25056     {
25057         if(this.inputType != 'radio'){
25058             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25059             return;
25060         }
25061         
25062         var _this = this;
25063         
25064         if(this.rendered){
25065             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25066                 _this.getActionEl().removeClass(this.disabledClass);
25067                 e.dom.disabled = false;
25068             });
25069         }
25070         
25071         this.disabled = false;
25072         this.fireEvent("enable", this);
25073         return this;
25074     },
25075     
25076     setBoxLabel : function(v)
25077     {
25078         this.boxLabel = v;
25079         
25080         if(this.rendered){
25081             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25082         }
25083     }
25084
25085 });
25086
25087 Roo.apply(Roo.bootstrap.CheckBox, {
25088     
25089     groups: {},
25090     
25091      /**
25092     * register a CheckBox Group
25093     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25094     */
25095     register : function(checkbox)
25096     {
25097         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25098             this.groups[checkbox.groupId] = {};
25099         }
25100         
25101         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25102             return;
25103         }
25104         
25105         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25106         
25107     },
25108     /**
25109     * fetch a CheckBox Group based on the group ID
25110     * @param {string} the group ID
25111     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25112     */
25113     get: function(groupId) {
25114         if (typeof(this.groups[groupId]) == 'undefined') {
25115             return false;
25116         }
25117         
25118         return this.groups[groupId] ;
25119     }
25120     
25121     
25122 });
25123 /*
25124  * - LGPL
25125  *
25126  * RadioItem
25127  * 
25128  */
25129
25130 /**
25131  * @class Roo.bootstrap.Radio
25132  * @extends Roo.bootstrap.Component
25133  * Bootstrap Radio class
25134  * @cfg {String} boxLabel - the label associated
25135  * @cfg {String} value - the value of radio
25136  * 
25137  * @constructor
25138  * Create a new Radio
25139  * @param {Object} config The config object
25140  */
25141 Roo.bootstrap.Radio = function(config){
25142     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25143     
25144 };
25145
25146 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25147     
25148     boxLabel : '',
25149     
25150     value : '',
25151     
25152     getAutoCreate : function()
25153     {
25154         var cfg = {
25155             tag : 'div',
25156             cls : 'form-group radio',
25157             cn : [
25158                 {
25159                     tag : 'label',
25160                     cls : 'box-label',
25161                     html : this.boxLabel
25162                 }
25163             ]
25164         };
25165         
25166         return cfg;
25167     },
25168     
25169     initEvents : function() 
25170     {
25171         this.parent().register(this);
25172         
25173         this.el.on('click', this.onClick, this);
25174         
25175     },
25176     
25177     onClick : function(e)
25178     {
25179         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25180             this.setChecked(true);
25181         }
25182     },
25183     
25184     setChecked : function(state, suppressEvent)
25185     {
25186         this.parent().setValue(this.value, suppressEvent);
25187         
25188     },
25189     
25190     setBoxLabel : function(v)
25191     {
25192         this.boxLabel = v;
25193         
25194         if(this.rendered){
25195             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25196         }
25197     }
25198     
25199 });
25200  
25201
25202  /*
25203  * - LGPL
25204  *
25205  * Input
25206  * 
25207  */
25208
25209 /**
25210  * @class Roo.bootstrap.SecurePass
25211  * @extends Roo.bootstrap.Input
25212  * Bootstrap SecurePass class
25213  *
25214  * 
25215  * @constructor
25216  * Create a new SecurePass
25217  * @param {Object} config The config object
25218  */
25219  
25220 Roo.bootstrap.SecurePass = function (config) {
25221     // these go here, so the translation tool can replace them..
25222     this.errors = {
25223         PwdEmpty: "Please type a password, and then retype it to confirm.",
25224         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25225         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25226         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25227         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25228         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25229         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25230         TooWeak: "Your password is Too Weak."
25231     },
25232     this.meterLabel = "Password strength:";
25233     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25234     this.meterClass = [
25235         "roo-password-meter-tooweak", 
25236         "roo-password-meter-weak", 
25237         "roo-password-meter-medium", 
25238         "roo-password-meter-strong", 
25239         "roo-password-meter-grey"
25240     ];
25241     
25242     this.errors = {};
25243     
25244     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25245 }
25246
25247 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25248     /**
25249      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25250      * {
25251      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25252      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25253      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25254      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25255      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25256      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25257      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25258      * })
25259      */
25260     // private
25261     
25262     meterWidth: 300,
25263     errorMsg :'',    
25264     errors: false,
25265     imageRoot: '/',
25266     /**
25267      * @cfg {String/Object} Label for the strength meter (defaults to
25268      * 'Password strength:')
25269      */
25270     // private
25271     meterLabel: '',
25272     /**
25273      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25274      * ['Weak', 'Medium', 'Strong'])
25275      */
25276     // private    
25277     pwdStrengths: false,    
25278     // private
25279     strength: 0,
25280     // private
25281     _lastPwd: null,
25282     // private
25283     kCapitalLetter: 0,
25284     kSmallLetter: 1,
25285     kDigit: 2,
25286     kPunctuation: 3,
25287     
25288     insecure: false,
25289     // private
25290     initEvents: function ()
25291     {
25292         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25293
25294         if (this.el.is('input[type=password]') && Roo.isSafari) {
25295             this.el.on('keydown', this.SafariOnKeyDown, this);
25296         }
25297
25298         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25299     },
25300     // private
25301     onRender: function (ct, position)
25302     {
25303         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25304         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25305         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25306
25307         this.trigger.createChild({
25308                    cn: [
25309                     {
25310                     //id: 'PwdMeter',
25311                     tag: 'div',
25312                     cls: 'roo-password-meter-grey col-xs-12',
25313                     style: {
25314                         //width: 0,
25315                         //width: this.meterWidth + 'px'                                                
25316                         }
25317                     },
25318                     {                            
25319                          cls: 'roo-password-meter-text'                          
25320                     }
25321                 ]            
25322         });
25323
25324          
25325         if (this.hideTrigger) {
25326             this.trigger.setDisplayed(false);
25327         }
25328         this.setSize(this.width || '', this.height || '');
25329     },
25330     // private
25331     onDestroy: function ()
25332     {
25333         if (this.trigger) {
25334             this.trigger.removeAllListeners();
25335             this.trigger.remove();
25336         }
25337         if (this.wrap) {
25338             this.wrap.remove();
25339         }
25340         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25341     },
25342     // private
25343     checkStrength: function ()
25344     {
25345         var pwd = this.inputEl().getValue();
25346         if (pwd == this._lastPwd) {
25347             return;
25348         }
25349
25350         var strength;
25351         if (this.ClientSideStrongPassword(pwd)) {
25352             strength = 3;
25353         } else if (this.ClientSideMediumPassword(pwd)) {
25354             strength = 2;
25355         } else if (this.ClientSideWeakPassword(pwd)) {
25356             strength = 1;
25357         } else {
25358             strength = 0;
25359         }
25360         
25361         Roo.log('strength1: ' + strength);
25362         
25363         //var pm = this.trigger.child('div/div/div').dom;
25364         var pm = this.trigger.child('div/div');
25365         pm.removeClass(this.meterClass);
25366         pm.addClass(this.meterClass[strength]);
25367                 
25368         
25369         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25370                 
25371         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25372         
25373         this._lastPwd = pwd;
25374     },
25375     reset: function ()
25376     {
25377         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25378         
25379         this._lastPwd = '';
25380         
25381         var pm = this.trigger.child('div/div');
25382         pm.removeClass(this.meterClass);
25383         pm.addClass('roo-password-meter-grey');        
25384         
25385         
25386         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25387         
25388         pt.innerHTML = '';
25389         this.inputEl().dom.type='password';
25390     },
25391     // private
25392     validateValue: function (value)
25393     {
25394         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25395             return false;
25396         }
25397         if (value.length == 0) {
25398             if (this.allowBlank) {
25399                 this.clearInvalid();
25400                 return true;
25401             }
25402
25403             this.markInvalid(this.errors.PwdEmpty);
25404             this.errorMsg = this.errors.PwdEmpty;
25405             return false;
25406         }
25407         
25408         if(this.insecure){
25409             return true;
25410         }
25411         
25412         if (!value.match(/[\x21-\x7e]+/)) {
25413             this.markInvalid(this.errors.PwdBadChar);
25414             this.errorMsg = this.errors.PwdBadChar;
25415             return false;
25416         }
25417         if (value.length < 6) {
25418             this.markInvalid(this.errors.PwdShort);
25419             this.errorMsg = this.errors.PwdShort;
25420             return false;
25421         }
25422         if (value.length > 16) {
25423             this.markInvalid(this.errors.PwdLong);
25424             this.errorMsg = this.errors.PwdLong;
25425             return false;
25426         }
25427         var strength;
25428         if (this.ClientSideStrongPassword(value)) {
25429             strength = 3;
25430         } else if (this.ClientSideMediumPassword(value)) {
25431             strength = 2;
25432         } else if (this.ClientSideWeakPassword(value)) {
25433             strength = 1;
25434         } else {
25435             strength = 0;
25436         }
25437
25438         
25439         if (strength < 2) {
25440             //this.markInvalid(this.errors.TooWeak);
25441             this.errorMsg = this.errors.TooWeak;
25442             //return false;
25443         }
25444         
25445         
25446         console.log('strength2: ' + strength);
25447         
25448         //var pm = this.trigger.child('div/div/div').dom;
25449         
25450         var pm = this.trigger.child('div/div');
25451         pm.removeClass(this.meterClass);
25452         pm.addClass(this.meterClass[strength]);
25453                 
25454         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25455                 
25456         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25457         
25458         this.errorMsg = ''; 
25459         return true;
25460     },
25461     // private
25462     CharacterSetChecks: function (type)
25463     {
25464         this.type = type;
25465         this.fResult = false;
25466     },
25467     // private
25468     isctype: function (character, type)
25469     {
25470         switch (type) {  
25471             case this.kCapitalLetter:
25472                 if (character >= 'A' && character <= 'Z') {
25473                     return true;
25474                 }
25475                 break;
25476             
25477             case this.kSmallLetter:
25478                 if (character >= 'a' && character <= 'z') {
25479                     return true;
25480                 }
25481                 break;
25482             
25483             case this.kDigit:
25484                 if (character >= '0' && character <= '9') {
25485                     return true;
25486                 }
25487                 break;
25488             
25489             case this.kPunctuation:
25490                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25491                     return true;
25492                 }
25493                 break;
25494             
25495             default:
25496                 return false;
25497         }
25498
25499     },
25500     // private
25501     IsLongEnough: function (pwd, size)
25502     {
25503         return !(pwd == null || isNaN(size) || pwd.length < size);
25504     },
25505     // private
25506     SpansEnoughCharacterSets: function (word, nb)
25507     {
25508         if (!this.IsLongEnough(word, nb))
25509         {
25510             return false;
25511         }
25512
25513         var characterSetChecks = new Array(
25514             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25515             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25516         );
25517         
25518         for (var index = 0; index < word.length; ++index) {
25519             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25520                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25521                     characterSetChecks[nCharSet].fResult = true;
25522                     break;
25523                 }
25524             }
25525         }
25526
25527         var nCharSets = 0;
25528         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25529             if (characterSetChecks[nCharSet].fResult) {
25530                 ++nCharSets;
25531             }
25532         }
25533
25534         if (nCharSets < nb) {
25535             return false;
25536         }
25537         return true;
25538     },
25539     // private
25540     ClientSideStrongPassword: function (pwd)
25541     {
25542         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25543     },
25544     // private
25545     ClientSideMediumPassword: function (pwd)
25546     {
25547         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25548     },
25549     // private
25550     ClientSideWeakPassword: function (pwd)
25551     {
25552         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25553     }
25554           
25555 })//<script type="text/javascript">
25556
25557 /*
25558  * Based  Ext JS Library 1.1.1
25559  * Copyright(c) 2006-2007, Ext JS, LLC.
25560  * LGPL
25561  *
25562  */
25563  
25564 /**
25565  * @class Roo.HtmlEditorCore
25566  * @extends Roo.Component
25567  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25568  *
25569  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25570  */
25571
25572 Roo.HtmlEditorCore = function(config){
25573     
25574     
25575     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25576     
25577     
25578     this.addEvents({
25579         /**
25580          * @event initialize
25581          * Fires when the editor is fully initialized (including the iframe)
25582          * @param {Roo.HtmlEditorCore} this
25583          */
25584         initialize: true,
25585         /**
25586          * @event activate
25587          * Fires when the editor is first receives the focus. Any insertion must wait
25588          * until after this event.
25589          * @param {Roo.HtmlEditorCore} this
25590          */
25591         activate: true,
25592          /**
25593          * @event beforesync
25594          * Fires before the textarea is updated with content from the editor iframe. Return false
25595          * to cancel the sync.
25596          * @param {Roo.HtmlEditorCore} this
25597          * @param {String} html
25598          */
25599         beforesync: true,
25600          /**
25601          * @event beforepush
25602          * Fires before the iframe editor is updated with content from the textarea. Return false
25603          * to cancel the push.
25604          * @param {Roo.HtmlEditorCore} this
25605          * @param {String} html
25606          */
25607         beforepush: true,
25608          /**
25609          * @event sync
25610          * Fires when the textarea is updated with content from the editor iframe.
25611          * @param {Roo.HtmlEditorCore} this
25612          * @param {String} html
25613          */
25614         sync: true,
25615          /**
25616          * @event push
25617          * Fires when the iframe editor is updated with content from the textarea.
25618          * @param {Roo.HtmlEditorCore} this
25619          * @param {String} html
25620          */
25621         push: true,
25622         
25623         /**
25624          * @event editorevent
25625          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25626          * @param {Roo.HtmlEditorCore} this
25627          */
25628         editorevent: true
25629         
25630     });
25631     
25632     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25633     
25634     // defaults : white / black...
25635     this.applyBlacklists();
25636     
25637     
25638     
25639 };
25640
25641
25642 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25643
25644
25645      /**
25646      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25647      */
25648     
25649     owner : false,
25650     
25651      /**
25652      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25653      *                        Roo.resizable.
25654      */
25655     resizable : false,
25656      /**
25657      * @cfg {Number} height (in pixels)
25658      */   
25659     height: 300,
25660    /**
25661      * @cfg {Number} width (in pixels)
25662      */   
25663     width: 500,
25664     
25665     /**
25666      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25667      * 
25668      */
25669     stylesheets: false,
25670     
25671     /**
25672      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25673      */
25674     allowComments: false,
25675     // id of frame..
25676     frameId: false,
25677     
25678     // private properties
25679     validationEvent : false,
25680     deferHeight: true,
25681     initialized : false,
25682     activated : false,
25683     sourceEditMode : false,
25684     onFocus : Roo.emptyFn,
25685     iframePad:3,
25686     hideMode:'offsets',
25687     
25688     clearUp: true,
25689     
25690     // blacklist + whitelisted elements..
25691     black: false,
25692     white: false,
25693      
25694     bodyCls : '',
25695
25696     /**
25697      * Protected method that will not generally be called directly. It
25698      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25699      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25700      */
25701     getDocMarkup : function(){
25702         // body styles..
25703         var st = '';
25704         
25705         // inherit styels from page...?? 
25706         if (this.stylesheets === false) {
25707             
25708             Roo.get(document.head).select('style').each(function(node) {
25709                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25710             });
25711             
25712             Roo.get(document.head).select('link').each(function(node) { 
25713                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25714             });
25715             
25716         } else if (!this.stylesheets.length) {
25717                 // simple..
25718                 st = '<style type="text/css">' +
25719                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25720                    '</style>';
25721         } else {
25722             for (var i in this.stylesheets) { 
25723                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25724             }
25725             
25726         }
25727         
25728         st +=  '<style type="text/css">' +
25729             'IMG { cursor: pointer } ' +
25730         '</style>';
25731
25732         var cls = 'roo-htmleditor-body';
25733         
25734         if(this.bodyCls.length){
25735             cls += ' ' + this.bodyCls;
25736         }
25737         
25738         return '<html><head>' + st  +
25739             //<style type="text/css">' +
25740             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25741             //'</style>' +
25742             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25743     },
25744
25745     // private
25746     onRender : function(ct, position)
25747     {
25748         var _t = this;
25749         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25750         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25751         
25752         
25753         this.el.dom.style.border = '0 none';
25754         this.el.dom.setAttribute('tabIndex', -1);
25755         this.el.addClass('x-hidden hide');
25756         
25757         
25758         
25759         if(Roo.isIE){ // fix IE 1px bogus margin
25760             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25761         }
25762        
25763         
25764         this.frameId = Roo.id();
25765         
25766          
25767         
25768         var iframe = this.owner.wrap.createChild({
25769             tag: 'iframe',
25770             cls: 'form-control', // bootstrap..
25771             id: this.frameId,
25772             name: this.frameId,
25773             frameBorder : 'no',
25774             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25775         }, this.el
25776         );
25777         
25778         
25779         this.iframe = iframe.dom;
25780
25781          this.assignDocWin();
25782         
25783         this.doc.designMode = 'on';
25784        
25785         this.doc.open();
25786         this.doc.write(this.getDocMarkup());
25787         this.doc.close();
25788
25789         
25790         var task = { // must defer to wait for browser to be ready
25791             run : function(){
25792                 //console.log("run task?" + this.doc.readyState);
25793                 this.assignDocWin();
25794                 if(this.doc.body || this.doc.readyState == 'complete'){
25795                     try {
25796                         this.doc.designMode="on";
25797                     } catch (e) {
25798                         return;
25799                     }
25800                     Roo.TaskMgr.stop(task);
25801                     this.initEditor.defer(10, this);
25802                 }
25803             },
25804             interval : 10,
25805             duration: 10000,
25806             scope: this
25807         };
25808         Roo.TaskMgr.start(task);
25809
25810     },
25811
25812     // private
25813     onResize : function(w, h)
25814     {
25815          Roo.log('resize: ' +w + ',' + h );
25816         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25817         if(!this.iframe){
25818             return;
25819         }
25820         if(typeof w == 'number'){
25821             
25822             this.iframe.style.width = w + 'px';
25823         }
25824         if(typeof h == 'number'){
25825             
25826             this.iframe.style.height = h + 'px';
25827             if(this.doc){
25828                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25829             }
25830         }
25831         
25832     },
25833
25834     /**
25835      * Toggles the editor between standard and source edit mode.
25836      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25837      */
25838     toggleSourceEdit : function(sourceEditMode){
25839         
25840         this.sourceEditMode = sourceEditMode === true;
25841         
25842         if(this.sourceEditMode){
25843  
25844             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25845             
25846         }else{
25847             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25848             //this.iframe.className = '';
25849             this.deferFocus();
25850         }
25851         //this.setSize(this.owner.wrap.getSize());
25852         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25853     },
25854
25855     
25856   
25857
25858     /**
25859      * Protected method that will not generally be called directly. If you need/want
25860      * custom HTML cleanup, this is the method you should override.
25861      * @param {String} html The HTML to be cleaned
25862      * return {String} The cleaned HTML
25863      */
25864     cleanHtml : function(html){
25865         html = String(html);
25866         if(html.length > 5){
25867             if(Roo.isSafari){ // strip safari nonsense
25868                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25869             }
25870         }
25871         if(html == '&nbsp;'){
25872             html = '';
25873         }
25874         return html;
25875     },
25876
25877     /**
25878      * HTML Editor -> Textarea
25879      * Protected method that will not generally be called directly. Syncs the contents
25880      * of the editor iframe with the textarea.
25881      */
25882     syncValue : function(){
25883         if(this.initialized){
25884             var bd = (this.doc.body || this.doc.documentElement);
25885             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25886             var html = bd.innerHTML;
25887             if(Roo.isSafari){
25888                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25889                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25890                 if(m && m[1]){
25891                     html = '<div style="'+m[0]+'">' + html + '</div>';
25892                 }
25893             }
25894             html = this.cleanHtml(html);
25895             // fix up the special chars.. normaly like back quotes in word...
25896             // however we do not want to do this with chinese..
25897             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25898                 
25899                 var cc = match.charCodeAt();
25900
25901                 // Get the character value, handling surrogate pairs
25902                 if (match.length == 2) {
25903                     // It's a surrogate pair, calculate the Unicode code point
25904                     var high = match.charCodeAt(0) - 0xD800;
25905                     var low  = match.charCodeAt(1) - 0xDC00;
25906                     cc = (high * 0x400) + low + 0x10000;
25907                 }  else if (
25908                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25909                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25910                     (cc >= 0xf900 && cc < 0xfb00 )
25911                 ) {
25912                         return match;
25913                 }  
25914          
25915                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25916                 return "&#" + cc + ";";
25917                 
25918                 
25919             });
25920             
25921             
25922              
25923             if(this.owner.fireEvent('beforesync', this, html) !== false){
25924                 this.el.dom.value = html;
25925                 this.owner.fireEvent('sync', this, html);
25926             }
25927         }
25928     },
25929
25930     /**
25931      * Protected method that will not generally be called directly. Pushes the value of the textarea
25932      * into the iframe editor.
25933      */
25934     pushValue : function(){
25935         if(this.initialized){
25936             var v = this.el.dom.value.trim();
25937             
25938 //            if(v.length < 1){
25939 //                v = '&#160;';
25940 //            }
25941             
25942             if(this.owner.fireEvent('beforepush', this, v) !== false){
25943                 var d = (this.doc.body || this.doc.documentElement);
25944                 d.innerHTML = v;
25945                 this.cleanUpPaste();
25946                 this.el.dom.value = d.innerHTML;
25947                 this.owner.fireEvent('push', this, v);
25948             }
25949         }
25950     },
25951
25952     // private
25953     deferFocus : function(){
25954         this.focus.defer(10, this);
25955     },
25956
25957     // doc'ed in Field
25958     focus : function(){
25959         if(this.win && !this.sourceEditMode){
25960             this.win.focus();
25961         }else{
25962             this.el.focus();
25963         }
25964     },
25965     
25966     assignDocWin: function()
25967     {
25968         var iframe = this.iframe;
25969         
25970          if(Roo.isIE){
25971             this.doc = iframe.contentWindow.document;
25972             this.win = iframe.contentWindow;
25973         } else {
25974 //            if (!Roo.get(this.frameId)) {
25975 //                return;
25976 //            }
25977 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25978 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25979             
25980             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25981                 return;
25982             }
25983             
25984             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25985             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25986         }
25987     },
25988     
25989     // private
25990     initEditor : function(){
25991         //console.log("INIT EDITOR");
25992         this.assignDocWin();
25993         
25994         
25995         
25996         this.doc.designMode="on";
25997         this.doc.open();
25998         this.doc.write(this.getDocMarkup());
25999         this.doc.close();
26000         
26001         var dbody = (this.doc.body || this.doc.documentElement);
26002         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26003         // this copies styles from the containing element into thsi one..
26004         // not sure why we need all of this..
26005         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26006         
26007         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26008         //ss['background-attachment'] = 'fixed'; // w3c
26009         dbody.bgProperties = 'fixed'; // ie
26010         //Roo.DomHelper.applyStyles(dbody, ss);
26011         Roo.EventManager.on(this.doc, {
26012             //'mousedown': this.onEditorEvent,
26013             'mouseup': this.onEditorEvent,
26014             'dblclick': this.onEditorEvent,
26015             'click': this.onEditorEvent,
26016             'keyup': this.onEditorEvent,
26017             buffer:100,
26018             scope: this
26019         });
26020         if(Roo.isGecko){
26021             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26022         }
26023         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26024             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26025         }
26026         this.initialized = true;
26027
26028         this.owner.fireEvent('initialize', this);
26029         this.pushValue();
26030     },
26031
26032     // private
26033     onDestroy : function(){
26034         
26035         
26036         
26037         if(this.rendered){
26038             
26039             //for (var i =0; i < this.toolbars.length;i++) {
26040             //    // fixme - ask toolbars for heights?
26041             //    this.toolbars[i].onDestroy();
26042            // }
26043             
26044             //this.wrap.dom.innerHTML = '';
26045             //this.wrap.remove();
26046         }
26047     },
26048
26049     // private
26050     onFirstFocus : function(){
26051         
26052         this.assignDocWin();
26053         
26054         
26055         this.activated = true;
26056          
26057     
26058         if(Roo.isGecko){ // prevent silly gecko errors
26059             this.win.focus();
26060             var s = this.win.getSelection();
26061             if(!s.focusNode || s.focusNode.nodeType != 3){
26062                 var r = s.getRangeAt(0);
26063                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26064                 r.collapse(true);
26065                 this.deferFocus();
26066             }
26067             try{
26068                 this.execCmd('useCSS', true);
26069                 this.execCmd('styleWithCSS', false);
26070             }catch(e){}
26071         }
26072         this.owner.fireEvent('activate', this);
26073     },
26074
26075     // private
26076     adjustFont: function(btn){
26077         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26078         //if(Roo.isSafari){ // safari
26079         //    adjust *= 2;
26080        // }
26081         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26082         if(Roo.isSafari){ // safari
26083             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26084             v =  (v < 10) ? 10 : v;
26085             v =  (v > 48) ? 48 : v;
26086             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26087             
26088         }
26089         
26090         
26091         v = Math.max(1, v+adjust);
26092         
26093         this.execCmd('FontSize', v  );
26094     },
26095
26096     onEditorEvent : function(e)
26097     {
26098         this.owner.fireEvent('editorevent', this, e);
26099       //  this.updateToolbar();
26100         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26101     },
26102
26103     insertTag : function(tg)
26104     {
26105         // could be a bit smarter... -> wrap the current selected tRoo..
26106         if (tg.toLowerCase() == 'span' ||
26107             tg.toLowerCase() == 'code' ||
26108             tg.toLowerCase() == 'sup' ||
26109             tg.toLowerCase() == 'sub' 
26110             ) {
26111             
26112             range = this.createRange(this.getSelection());
26113             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26114             wrappingNode.appendChild(range.extractContents());
26115             range.insertNode(wrappingNode);
26116
26117             return;
26118             
26119             
26120             
26121         }
26122         this.execCmd("formatblock",   tg);
26123         
26124     },
26125     
26126     insertText : function(txt)
26127     {
26128         
26129         
26130         var range = this.createRange();
26131         range.deleteContents();
26132                //alert(Sender.getAttribute('label'));
26133                
26134         range.insertNode(this.doc.createTextNode(txt));
26135     } ,
26136     
26137      
26138
26139     /**
26140      * Executes a Midas editor command on the editor document and performs necessary focus and
26141      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26142      * @param {String} cmd The Midas command
26143      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26144      */
26145     relayCmd : function(cmd, value){
26146         this.win.focus();
26147         this.execCmd(cmd, value);
26148         this.owner.fireEvent('editorevent', this);
26149         //this.updateToolbar();
26150         this.owner.deferFocus();
26151     },
26152
26153     /**
26154      * Executes a Midas editor command directly on the editor document.
26155      * For visual commands, you should use {@link #relayCmd} instead.
26156      * <b>This should only be called after the editor is initialized.</b>
26157      * @param {String} cmd The Midas command
26158      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26159      */
26160     execCmd : function(cmd, value){
26161         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26162         this.syncValue();
26163     },
26164  
26165  
26166    
26167     /**
26168      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26169      * to insert tRoo.
26170      * @param {String} text | dom node.. 
26171      */
26172     insertAtCursor : function(text)
26173     {
26174         
26175         if(!this.activated){
26176             return;
26177         }
26178         /*
26179         if(Roo.isIE){
26180             this.win.focus();
26181             var r = this.doc.selection.createRange();
26182             if(r){
26183                 r.collapse(true);
26184                 r.pasteHTML(text);
26185                 this.syncValue();
26186                 this.deferFocus();
26187             
26188             }
26189             return;
26190         }
26191         */
26192         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26193             this.win.focus();
26194             
26195             
26196             // from jquery ui (MIT licenced)
26197             var range, node;
26198             var win = this.win;
26199             
26200             if (win.getSelection && win.getSelection().getRangeAt) {
26201                 range = win.getSelection().getRangeAt(0);
26202                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26203                 range.insertNode(node);
26204             } else if (win.document.selection && win.document.selection.createRange) {
26205                 // no firefox support
26206                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26207                 win.document.selection.createRange().pasteHTML(txt);
26208             } else {
26209                 // no firefox support
26210                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26211                 this.execCmd('InsertHTML', txt);
26212             } 
26213             
26214             this.syncValue();
26215             
26216             this.deferFocus();
26217         }
26218     },
26219  // private
26220     mozKeyPress : function(e){
26221         if(e.ctrlKey){
26222             var c = e.getCharCode(), cmd;
26223           
26224             if(c > 0){
26225                 c = String.fromCharCode(c).toLowerCase();
26226                 switch(c){
26227                     case 'b':
26228                         cmd = 'bold';
26229                         break;
26230                     case 'i':
26231                         cmd = 'italic';
26232                         break;
26233                     
26234                     case 'u':
26235                         cmd = 'underline';
26236                         break;
26237                     
26238                     case 'v':
26239                         this.cleanUpPaste.defer(100, this);
26240                         return;
26241                         
26242                 }
26243                 if(cmd){
26244                     this.win.focus();
26245                     this.execCmd(cmd);
26246                     this.deferFocus();
26247                     e.preventDefault();
26248                 }
26249                 
26250             }
26251         }
26252     },
26253
26254     // private
26255     fixKeys : function(){ // load time branching for fastest keydown performance
26256         if(Roo.isIE){
26257             return function(e){
26258                 var k = e.getKey(), r;
26259                 if(k == e.TAB){
26260                     e.stopEvent();
26261                     r = this.doc.selection.createRange();
26262                     if(r){
26263                         r.collapse(true);
26264                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26265                         this.deferFocus();
26266                     }
26267                     return;
26268                 }
26269                 
26270                 if(k == e.ENTER){
26271                     r = this.doc.selection.createRange();
26272                     if(r){
26273                         var target = r.parentElement();
26274                         if(!target || target.tagName.toLowerCase() != 'li'){
26275                             e.stopEvent();
26276                             r.pasteHTML('<br />');
26277                             r.collapse(false);
26278                             r.select();
26279                         }
26280                     }
26281                 }
26282                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26283                     this.cleanUpPaste.defer(100, this);
26284                     return;
26285                 }
26286                 
26287                 
26288             };
26289         }else if(Roo.isOpera){
26290             return function(e){
26291                 var k = e.getKey();
26292                 if(k == e.TAB){
26293                     e.stopEvent();
26294                     this.win.focus();
26295                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26296                     this.deferFocus();
26297                 }
26298                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26299                     this.cleanUpPaste.defer(100, this);
26300                     return;
26301                 }
26302                 
26303             };
26304         }else if(Roo.isSafari){
26305             return function(e){
26306                 var k = e.getKey();
26307                 
26308                 if(k == e.TAB){
26309                     e.stopEvent();
26310                     this.execCmd('InsertText','\t');
26311                     this.deferFocus();
26312                     return;
26313                 }
26314                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26315                     this.cleanUpPaste.defer(100, this);
26316                     return;
26317                 }
26318                 
26319              };
26320         }
26321     }(),
26322     
26323     getAllAncestors: function()
26324     {
26325         var p = this.getSelectedNode();
26326         var a = [];
26327         if (!p) {
26328             a.push(p); // push blank onto stack..
26329             p = this.getParentElement();
26330         }
26331         
26332         
26333         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26334             a.push(p);
26335             p = p.parentNode;
26336         }
26337         a.push(this.doc.body);
26338         return a;
26339     },
26340     lastSel : false,
26341     lastSelNode : false,
26342     
26343     
26344     getSelection : function() 
26345     {
26346         this.assignDocWin();
26347         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26348     },
26349     
26350     getSelectedNode: function() 
26351     {
26352         // this may only work on Gecko!!!
26353         
26354         // should we cache this!!!!
26355         
26356         
26357         
26358          
26359         var range = this.createRange(this.getSelection()).cloneRange();
26360         
26361         if (Roo.isIE) {
26362             var parent = range.parentElement();
26363             while (true) {
26364                 var testRange = range.duplicate();
26365                 testRange.moveToElementText(parent);
26366                 if (testRange.inRange(range)) {
26367                     break;
26368                 }
26369                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26370                     break;
26371                 }
26372                 parent = parent.parentElement;
26373             }
26374             return parent;
26375         }
26376         
26377         // is ancestor a text element.
26378         var ac =  range.commonAncestorContainer;
26379         if (ac.nodeType == 3) {
26380             ac = ac.parentNode;
26381         }
26382         
26383         var ar = ac.childNodes;
26384          
26385         var nodes = [];
26386         var other_nodes = [];
26387         var has_other_nodes = false;
26388         for (var i=0;i<ar.length;i++) {
26389             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26390                 continue;
26391             }
26392             // fullly contained node.
26393             
26394             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26395                 nodes.push(ar[i]);
26396                 continue;
26397             }
26398             
26399             // probably selected..
26400             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26401                 other_nodes.push(ar[i]);
26402                 continue;
26403             }
26404             // outer..
26405             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26406                 continue;
26407             }
26408             
26409             
26410             has_other_nodes = true;
26411         }
26412         if (!nodes.length && other_nodes.length) {
26413             nodes= other_nodes;
26414         }
26415         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26416             return false;
26417         }
26418         
26419         return nodes[0];
26420     },
26421     createRange: function(sel)
26422     {
26423         // this has strange effects when using with 
26424         // top toolbar - not sure if it's a great idea.
26425         //this.editor.contentWindow.focus();
26426         if (typeof sel != "undefined") {
26427             try {
26428                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26429             } catch(e) {
26430                 return this.doc.createRange();
26431             }
26432         } else {
26433             return this.doc.createRange();
26434         }
26435     },
26436     getParentElement: function()
26437     {
26438         
26439         this.assignDocWin();
26440         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26441         
26442         var range = this.createRange(sel);
26443          
26444         try {
26445             var p = range.commonAncestorContainer;
26446             while (p.nodeType == 3) { // text node
26447                 p = p.parentNode;
26448             }
26449             return p;
26450         } catch (e) {
26451             return null;
26452         }
26453     
26454     },
26455     /***
26456      *
26457      * Range intersection.. the hard stuff...
26458      *  '-1' = before
26459      *  '0' = hits..
26460      *  '1' = after.
26461      *         [ -- selected range --- ]
26462      *   [fail]                        [fail]
26463      *
26464      *    basically..
26465      *      if end is before start or  hits it. fail.
26466      *      if start is after end or hits it fail.
26467      *
26468      *   if either hits (but other is outside. - then it's not 
26469      *   
26470      *    
26471      **/
26472     
26473     
26474     // @see http://www.thismuchiknow.co.uk/?p=64.
26475     rangeIntersectsNode : function(range, node)
26476     {
26477         var nodeRange = node.ownerDocument.createRange();
26478         try {
26479             nodeRange.selectNode(node);
26480         } catch (e) {
26481             nodeRange.selectNodeContents(node);
26482         }
26483     
26484         var rangeStartRange = range.cloneRange();
26485         rangeStartRange.collapse(true);
26486     
26487         var rangeEndRange = range.cloneRange();
26488         rangeEndRange.collapse(false);
26489     
26490         var nodeStartRange = nodeRange.cloneRange();
26491         nodeStartRange.collapse(true);
26492     
26493         var nodeEndRange = nodeRange.cloneRange();
26494         nodeEndRange.collapse(false);
26495     
26496         return rangeStartRange.compareBoundaryPoints(
26497                  Range.START_TO_START, nodeEndRange) == -1 &&
26498                rangeEndRange.compareBoundaryPoints(
26499                  Range.START_TO_START, nodeStartRange) == 1;
26500         
26501          
26502     },
26503     rangeCompareNode : function(range, node)
26504     {
26505         var nodeRange = node.ownerDocument.createRange();
26506         try {
26507             nodeRange.selectNode(node);
26508         } catch (e) {
26509             nodeRange.selectNodeContents(node);
26510         }
26511         
26512         
26513         range.collapse(true);
26514     
26515         nodeRange.collapse(true);
26516      
26517         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26518         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26519          
26520         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26521         
26522         var nodeIsBefore   =  ss == 1;
26523         var nodeIsAfter    = ee == -1;
26524         
26525         if (nodeIsBefore && nodeIsAfter) {
26526             return 0; // outer
26527         }
26528         if (!nodeIsBefore && nodeIsAfter) {
26529             return 1; //right trailed.
26530         }
26531         
26532         if (nodeIsBefore && !nodeIsAfter) {
26533             return 2;  // left trailed.
26534         }
26535         // fully contined.
26536         return 3;
26537     },
26538
26539     // private? - in a new class?
26540     cleanUpPaste :  function()
26541     {
26542         // cleans up the whole document..
26543         Roo.log('cleanuppaste');
26544         
26545         this.cleanUpChildren(this.doc.body);
26546         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26547         if (clean != this.doc.body.innerHTML) {
26548             this.doc.body.innerHTML = clean;
26549         }
26550         
26551     },
26552     
26553     cleanWordChars : function(input) {// change the chars to hex code
26554         var he = Roo.HtmlEditorCore;
26555         
26556         var output = input;
26557         Roo.each(he.swapCodes, function(sw) { 
26558             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26559             
26560             output = output.replace(swapper, sw[1]);
26561         });
26562         
26563         return output;
26564     },
26565     
26566     
26567     cleanUpChildren : function (n)
26568     {
26569         if (!n.childNodes.length) {
26570             return;
26571         }
26572         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26573            this.cleanUpChild(n.childNodes[i]);
26574         }
26575     },
26576     
26577     
26578         
26579     
26580     cleanUpChild : function (node)
26581     {
26582         var ed = this;
26583         //console.log(node);
26584         if (node.nodeName == "#text") {
26585             // clean up silly Windows -- stuff?
26586             return; 
26587         }
26588         if (node.nodeName == "#comment") {
26589             if (!this.allowComments) {
26590                 node.parentNode.removeChild(node);
26591             }
26592             // clean up silly Windows -- stuff?
26593             return; 
26594         }
26595         var lcname = node.tagName.toLowerCase();
26596         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26597         // whitelist of tags..
26598         
26599         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26600             // remove node.
26601             node.parentNode.removeChild(node);
26602             return;
26603             
26604         }
26605         
26606         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26607         
26608         // spans with no attributes - just remove them..
26609         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26610             remove_keep_children = true;
26611         }
26612         
26613         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26614         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26615         
26616         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26617         //    remove_keep_children = true;
26618         //}
26619         
26620         if (remove_keep_children) {
26621             this.cleanUpChildren(node);
26622             // inserts everything just before this node...
26623             while (node.childNodes.length) {
26624                 var cn = node.childNodes[0];
26625                 node.removeChild(cn);
26626                 node.parentNode.insertBefore(cn, node);
26627             }
26628             node.parentNode.removeChild(node);
26629             return;
26630         }
26631         
26632         if (!node.attributes || !node.attributes.length) {
26633             
26634           
26635             
26636             
26637             this.cleanUpChildren(node);
26638             return;
26639         }
26640         
26641         function cleanAttr(n,v)
26642         {
26643             
26644             if (v.match(/^\./) || v.match(/^\//)) {
26645                 return;
26646             }
26647             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26648                 return;
26649             }
26650             if (v.match(/^#/)) {
26651                 return;
26652             }
26653             if (v.match(/^\{/)) { // allow template editing.
26654                 return;
26655             }
26656 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26657             node.removeAttribute(n);
26658             
26659         }
26660         
26661         var cwhite = this.cwhite;
26662         var cblack = this.cblack;
26663             
26664         function cleanStyle(n,v)
26665         {
26666             if (v.match(/expression/)) { //XSS?? should we even bother..
26667                 node.removeAttribute(n);
26668                 return;
26669             }
26670             
26671             var parts = v.split(/;/);
26672             var clean = [];
26673             
26674             Roo.each(parts, function(p) {
26675                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26676                 if (!p.length) {
26677                     return true;
26678                 }
26679                 var l = p.split(':').shift().replace(/\s+/g,'');
26680                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26681                 
26682                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26683 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26684                     //node.removeAttribute(n);
26685                     return true;
26686                 }
26687                 //Roo.log()
26688                 // only allow 'c whitelisted system attributes'
26689                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26690 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26691                     //node.removeAttribute(n);
26692                     return true;
26693                 }
26694                 
26695                 
26696                  
26697                 
26698                 clean.push(p);
26699                 return true;
26700             });
26701             if (clean.length) { 
26702                 node.setAttribute(n, clean.join(';'));
26703             } else {
26704                 node.removeAttribute(n);
26705             }
26706             
26707         }
26708         
26709         
26710         for (var i = node.attributes.length-1; i > -1 ; i--) {
26711             var a = node.attributes[i];
26712             //console.log(a);
26713             
26714             if (a.name.toLowerCase().substr(0,2)=='on')  {
26715                 node.removeAttribute(a.name);
26716                 continue;
26717             }
26718             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26719                 node.removeAttribute(a.name);
26720                 continue;
26721             }
26722             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26723                 cleanAttr(a.name,a.value); // fixme..
26724                 continue;
26725             }
26726             if (a.name == 'style') {
26727                 cleanStyle(a.name,a.value);
26728                 continue;
26729             }
26730             /// clean up MS crap..
26731             // tecnically this should be a list of valid class'es..
26732             
26733             
26734             if (a.name == 'class') {
26735                 if (a.value.match(/^Mso/)) {
26736                     node.removeAttribute('class');
26737                 }
26738                 
26739                 if (a.value.match(/^body$/)) {
26740                     node.removeAttribute('class');
26741                 }
26742                 continue;
26743             }
26744             
26745             // style cleanup!?
26746             // class cleanup?
26747             
26748         }
26749         
26750         
26751         this.cleanUpChildren(node);
26752         
26753         
26754     },
26755     
26756     /**
26757      * Clean up MS wordisms...
26758      */
26759     cleanWord : function(node)
26760     {
26761         if (!node) {
26762             this.cleanWord(this.doc.body);
26763             return;
26764         }
26765         
26766         if(
26767                 node.nodeName == 'SPAN' &&
26768                 !node.hasAttributes() &&
26769                 node.childNodes.length == 1 &&
26770                 node.firstChild.nodeName == "#text"  
26771         ) {
26772             var textNode = node.firstChild;
26773             node.removeChild(textNode);
26774             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26775                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26776             }
26777             node.parentNode.insertBefore(textNode, node);
26778             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26779                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26780             }
26781             node.parentNode.removeChild(node);
26782         }
26783         
26784         if (node.nodeName == "#text") {
26785             // clean up silly Windows -- stuff?
26786             return; 
26787         }
26788         if (node.nodeName == "#comment") {
26789             node.parentNode.removeChild(node);
26790             // clean up silly Windows -- stuff?
26791             return; 
26792         }
26793         
26794         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26795             node.parentNode.removeChild(node);
26796             return;
26797         }
26798         //Roo.log(node.tagName);
26799         // remove - but keep children..
26800         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26801             //Roo.log('-- removed');
26802             while (node.childNodes.length) {
26803                 var cn = node.childNodes[0];
26804                 node.removeChild(cn);
26805                 node.parentNode.insertBefore(cn, node);
26806                 // move node to parent - and clean it..
26807                 this.cleanWord(cn);
26808             }
26809             node.parentNode.removeChild(node);
26810             /// no need to iterate chidlren = it's got none..
26811             //this.iterateChildren(node, this.cleanWord);
26812             return;
26813         }
26814         // clean styles
26815         if (node.className.length) {
26816             
26817             var cn = node.className.split(/\W+/);
26818             var cna = [];
26819             Roo.each(cn, function(cls) {
26820                 if (cls.match(/Mso[a-zA-Z]+/)) {
26821                     return;
26822                 }
26823                 cna.push(cls);
26824             });
26825             node.className = cna.length ? cna.join(' ') : '';
26826             if (!cna.length) {
26827                 node.removeAttribute("class");
26828             }
26829         }
26830         
26831         if (node.hasAttribute("lang")) {
26832             node.removeAttribute("lang");
26833         }
26834         
26835         if (node.hasAttribute("style")) {
26836             
26837             var styles = node.getAttribute("style").split(";");
26838             var nstyle = [];
26839             Roo.each(styles, function(s) {
26840                 if (!s.match(/:/)) {
26841                     return;
26842                 }
26843                 var kv = s.split(":");
26844                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26845                     return;
26846                 }
26847                 // what ever is left... we allow.
26848                 nstyle.push(s);
26849             });
26850             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26851             if (!nstyle.length) {
26852                 node.removeAttribute('style');
26853             }
26854         }
26855         this.iterateChildren(node, this.cleanWord);
26856         
26857         
26858         
26859     },
26860     /**
26861      * iterateChildren of a Node, calling fn each time, using this as the scole..
26862      * @param {DomNode} node node to iterate children of.
26863      * @param {Function} fn method of this class to call on each item.
26864      */
26865     iterateChildren : function(node, fn)
26866     {
26867         if (!node.childNodes.length) {
26868                 return;
26869         }
26870         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26871            fn.call(this, node.childNodes[i])
26872         }
26873     },
26874     
26875     
26876     /**
26877      * cleanTableWidths.
26878      *
26879      * Quite often pasting from word etc.. results in tables with column and widths.
26880      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26881      *
26882      */
26883     cleanTableWidths : function(node)
26884     {
26885          
26886          
26887         if (!node) {
26888             this.cleanTableWidths(this.doc.body);
26889             return;
26890         }
26891         
26892         // ignore list...
26893         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26894             return; 
26895         }
26896         Roo.log(node.tagName);
26897         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26898             this.iterateChildren(node, this.cleanTableWidths);
26899             return;
26900         }
26901         if (node.hasAttribute('width')) {
26902             node.removeAttribute('width');
26903         }
26904         
26905          
26906         if (node.hasAttribute("style")) {
26907             // pretty basic...
26908             
26909             var styles = node.getAttribute("style").split(";");
26910             var nstyle = [];
26911             Roo.each(styles, function(s) {
26912                 if (!s.match(/:/)) {
26913                     return;
26914                 }
26915                 var kv = s.split(":");
26916                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26917                     return;
26918                 }
26919                 // what ever is left... we allow.
26920                 nstyle.push(s);
26921             });
26922             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26923             if (!nstyle.length) {
26924                 node.removeAttribute('style');
26925             }
26926         }
26927         
26928         this.iterateChildren(node, this.cleanTableWidths);
26929         
26930         
26931     },
26932     
26933     
26934     
26935     
26936     domToHTML : function(currentElement, depth, nopadtext) {
26937         
26938         depth = depth || 0;
26939         nopadtext = nopadtext || false;
26940     
26941         if (!currentElement) {
26942             return this.domToHTML(this.doc.body);
26943         }
26944         
26945         //Roo.log(currentElement);
26946         var j;
26947         var allText = false;
26948         var nodeName = currentElement.nodeName;
26949         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26950         
26951         if  (nodeName == '#text') {
26952             
26953             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26954         }
26955         
26956         
26957         var ret = '';
26958         if (nodeName != 'BODY') {
26959              
26960             var i = 0;
26961             // Prints the node tagName, such as <A>, <IMG>, etc
26962             if (tagName) {
26963                 var attr = [];
26964                 for(i = 0; i < currentElement.attributes.length;i++) {
26965                     // quoting?
26966                     var aname = currentElement.attributes.item(i).name;
26967                     if (!currentElement.attributes.item(i).value.length) {
26968                         continue;
26969                     }
26970                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26971                 }
26972                 
26973                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26974             } 
26975             else {
26976                 
26977                 // eack
26978             }
26979         } else {
26980             tagName = false;
26981         }
26982         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26983             return ret;
26984         }
26985         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26986             nopadtext = true;
26987         }
26988         
26989         
26990         // Traverse the tree
26991         i = 0;
26992         var currentElementChild = currentElement.childNodes.item(i);
26993         var allText = true;
26994         var innerHTML  = '';
26995         lastnode = '';
26996         while (currentElementChild) {
26997             // Formatting code (indent the tree so it looks nice on the screen)
26998             var nopad = nopadtext;
26999             if (lastnode == 'SPAN') {
27000                 nopad  = true;
27001             }
27002             // text
27003             if  (currentElementChild.nodeName == '#text') {
27004                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27005                 toadd = nopadtext ? toadd : toadd.trim();
27006                 if (!nopad && toadd.length > 80) {
27007                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27008                 }
27009                 innerHTML  += toadd;
27010                 
27011                 i++;
27012                 currentElementChild = currentElement.childNodes.item(i);
27013                 lastNode = '';
27014                 continue;
27015             }
27016             allText = false;
27017             
27018             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27019                 
27020             // Recursively traverse the tree structure of the child node
27021             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27022             lastnode = currentElementChild.nodeName;
27023             i++;
27024             currentElementChild=currentElement.childNodes.item(i);
27025         }
27026         
27027         ret += innerHTML;
27028         
27029         if (!allText) {
27030                 // The remaining code is mostly for formatting the tree
27031             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27032         }
27033         
27034         
27035         if (tagName) {
27036             ret+= "</"+tagName+">";
27037         }
27038         return ret;
27039         
27040     },
27041         
27042     applyBlacklists : function()
27043     {
27044         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27045         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27046         
27047         this.white = [];
27048         this.black = [];
27049         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27050             if (b.indexOf(tag) > -1) {
27051                 return;
27052             }
27053             this.white.push(tag);
27054             
27055         }, this);
27056         
27057         Roo.each(w, function(tag) {
27058             if (b.indexOf(tag) > -1) {
27059                 return;
27060             }
27061             if (this.white.indexOf(tag) > -1) {
27062                 return;
27063             }
27064             this.white.push(tag);
27065             
27066         }, this);
27067         
27068         
27069         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27070             if (w.indexOf(tag) > -1) {
27071                 return;
27072             }
27073             this.black.push(tag);
27074             
27075         }, this);
27076         
27077         Roo.each(b, function(tag) {
27078             if (w.indexOf(tag) > -1) {
27079                 return;
27080             }
27081             if (this.black.indexOf(tag) > -1) {
27082                 return;
27083             }
27084             this.black.push(tag);
27085             
27086         }, this);
27087         
27088         
27089         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27090         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27091         
27092         this.cwhite = [];
27093         this.cblack = [];
27094         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27095             if (b.indexOf(tag) > -1) {
27096                 return;
27097             }
27098             this.cwhite.push(tag);
27099             
27100         }, this);
27101         
27102         Roo.each(w, function(tag) {
27103             if (b.indexOf(tag) > -1) {
27104                 return;
27105             }
27106             if (this.cwhite.indexOf(tag) > -1) {
27107                 return;
27108             }
27109             this.cwhite.push(tag);
27110             
27111         }, this);
27112         
27113         
27114         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27115             if (w.indexOf(tag) > -1) {
27116                 return;
27117             }
27118             this.cblack.push(tag);
27119             
27120         }, this);
27121         
27122         Roo.each(b, function(tag) {
27123             if (w.indexOf(tag) > -1) {
27124                 return;
27125             }
27126             if (this.cblack.indexOf(tag) > -1) {
27127                 return;
27128             }
27129             this.cblack.push(tag);
27130             
27131         }, this);
27132     },
27133     
27134     setStylesheets : function(stylesheets)
27135     {
27136         if(typeof(stylesheets) == 'string'){
27137             Roo.get(this.iframe.contentDocument.head).createChild({
27138                 tag : 'link',
27139                 rel : 'stylesheet',
27140                 type : 'text/css',
27141                 href : stylesheets
27142             });
27143             
27144             return;
27145         }
27146         var _this = this;
27147      
27148         Roo.each(stylesheets, function(s) {
27149             if(!s.length){
27150                 return;
27151             }
27152             
27153             Roo.get(_this.iframe.contentDocument.head).createChild({
27154                 tag : 'link',
27155                 rel : 'stylesheet',
27156                 type : 'text/css',
27157                 href : s
27158             });
27159         });
27160
27161         
27162     },
27163     
27164     removeStylesheets : function()
27165     {
27166         var _this = this;
27167         
27168         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27169             s.remove();
27170         });
27171     },
27172     
27173     setStyle : function(style)
27174     {
27175         Roo.get(this.iframe.contentDocument.head).createChild({
27176             tag : 'style',
27177             type : 'text/css',
27178             html : style
27179         });
27180
27181         return;
27182     }
27183     
27184     // hide stuff that is not compatible
27185     /**
27186      * @event blur
27187      * @hide
27188      */
27189     /**
27190      * @event change
27191      * @hide
27192      */
27193     /**
27194      * @event focus
27195      * @hide
27196      */
27197     /**
27198      * @event specialkey
27199      * @hide
27200      */
27201     /**
27202      * @cfg {String} fieldClass @hide
27203      */
27204     /**
27205      * @cfg {String} focusClass @hide
27206      */
27207     /**
27208      * @cfg {String} autoCreate @hide
27209      */
27210     /**
27211      * @cfg {String} inputType @hide
27212      */
27213     /**
27214      * @cfg {String} invalidClass @hide
27215      */
27216     /**
27217      * @cfg {String} invalidText @hide
27218      */
27219     /**
27220      * @cfg {String} msgFx @hide
27221      */
27222     /**
27223      * @cfg {String} validateOnBlur @hide
27224      */
27225 });
27226
27227 Roo.HtmlEditorCore.white = [
27228         'area', 'br', 'img', 'input', 'hr', 'wbr',
27229         
27230        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27231        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27232        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27233        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27234        'table',   'ul',         'xmp', 
27235        
27236        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27237       'thead',   'tr', 
27238      
27239       'dir', 'menu', 'ol', 'ul', 'dl',
27240        
27241       'embed',  'object'
27242 ];
27243
27244
27245 Roo.HtmlEditorCore.black = [
27246     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27247         'applet', // 
27248         'base',   'basefont', 'bgsound', 'blink',  'body', 
27249         'frame',  'frameset', 'head',    'html',   'ilayer', 
27250         'iframe', 'layer',  'link',     'meta',    'object',   
27251         'script', 'style' ,'title',  'xml' // clean later..
27252 ];
27253 Roo.HtmlEditorCore.clean = [
27254     'script', 'style', 'title', 'xml'
27255 ];
27256 Roo.HtmlEditorCore.remove = [
27257     'font'
27258 ];
27259 // attributes..
27260
27261 Roo.HtmlEditorCore.ablack = [
27262     'on'
27263 ];
27264     
27265 Roo.HtmlEditorCore.aclean = [ 
27266     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27267 ];
27268
27269 // protocols..
27270 Roo.HtmlEditorCore.pwhite= [
27271         'http',  'https',  'mailto'
27272 ];
27273
27274 // white listed style attributes.
27275 Roo.HtmlEditorCore.cwhite= [
27276       //  'text-align', /// default is to allow most things..
27277       
27278          
27279 //        'font-size'//??
27280 ];
27281
27282 // black listed style attributes.
27283 Roo.HtmlEditorCore.cblack= [
27284       //  'font-size' -- this can be set by the project 
27285 ];
27286
27287
27288 Roo.HtmlEditorCore.swapCodes   =[ 
27289     [    8211, "&#8211;" ], 
27290     [    8212, "&#8212;" ], 
27291     [    8216,  "'" ],  
27292     [    8217, "'" ],  
27293     [    8220, '"' ],  
27294     [    8221, '"' ],  
27295     [    8226, "*" ],  
27296     [    8230, "..." ]
27297 ]; 
27298
27299     /*
27300  * - LGPL
27301  *
27302  * HtmlEditor
27303  * 
27304  */
27305
27306 /**
27307  * @class Roo.bootstrap.HtmlEditor
27308  * @extends Roo.bootstrap.TextArea
27309  * Bootstrap HtmlEditor class
27310
27311  * @constructor
27312  * Create a new HtmlEditor
27313  * @param {Object} config The config object
27314  */
27315
27316 Roo.bootstrap.HtmlEditor = function(config){
27317     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27318     if (!this.toolbars) {
27319         this.toolbars = [];
27320     }
27321     
27322     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27323     this.addEvents({
27324             /**
27325              * @event initialize
27326              * Fires when the editor is fully initialized (including the iframe)
27327              * @param {HtmlEditor} this
27328              */
27329             initialize: true,
27330             /**
27331              * @event activate
27332              * Fires when the editor is first receives the focus. Any insertion must wait
27333              * until after this event.
27334              * @param {HtmlEditor} this
27335              */
27336             activate: true,
27337              /**
27338              * @event beforesync
27339              * Fires before the textarea is updated with content from the editor iframe. Return false
27340              * to cancel the sync.
27341              * @param {HtmlEditor} this
27342              * @param {String} html
27343              */
27344             beforesync: true,
27345              /**
27346              * @event beforepush
27347              * Fires before the iframe editor is updated with content from the textarea. Return false
27348              * to cancel the push.
27349              * @param {HtmlEditor} this
27350              * @param {String} html
27351              */
27352             beforepush: true,
27353              /**
27354              * @event sync
27355              * Fires when the textarea is updated with content from the editor iframe.
27356              * @param {HtmlEditor} this
27357              * @param {String} html
27358              */
27359             sync: true,
27360              /**
27361              * @event push
27362              * Fires when the iframe editor is updated with content from the textarea.
27363              * @param {HtmlEditor} this
27364              * @param {String} html
27365              */
27366             push: true,
27367              /**
27368              * @event editmodechange
27369              * Fires when the editor switches edit modes
27370              * @param {HtmlEditor} this
27371              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27372              */
27373             editmodechange: true,
27374             /**
27375              * @event editorevent
27376              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27377              * @param {HtmlEditor} this
27378              */
27379             editorevent: true,
27380             /**
27381              * @event firstfocus
27382              * Fires when on first focus - needed by toolbars..
27383              * @param {HtmlEditor} this
27384              */
27385             firstfocus: true,
27386             /**
27387              * @event autosave
27388              * Auto save the htmlEditor value as a file into Events
27389              * @param {HtmlEditor} this
27390              */
27391             autosave: true,
27392             /**
27393              * @event savedpreview
27394              * preview the saved version of htmlEditor
27395              * @param {HtmlEditor} this
27396              */
27397             savedpreview: true
27398         });
27399 };
27400
27401
27402 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27403     
27404     
27405       /**
27406      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27407      */
27408     toolbars : false,
27409     
27410      /**
27411     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27412     */
27413     btns : [],
27414    
27415      /**
27416      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27417      *                        Roo.resizable.
27418      */
27419     resizable : false,
27420      /**
27421      * @cfg {Number} height (in pixels)
27422      */   
27423     height: 300,
27424    /**
27425      * @cfg {Number} width (in pixels)
27426      */   
27427     width: false,
27428     
27429     /**
27430      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27431      * 
27432      */
27433     stylesheets: false,
27434     
27435     // id of frame..
27436     frameId: false,
27437     
27438     // private properties
27439     validationEvent : false,
27440     deferHeight: true,
27441     initialized : false,
27442     activated : false,
27443     
27444     onFocus : Roo.emptyFn,
27445     iframePad:3,
27446     hideMode:'offsets',
27447     
27448     tbContainer : false,
27449     
27450     bodyCls : '',
27451     
27452     toolbarContainer :function() {
27453         return this.wrap.select('.x-html-editor-tb',true).first();
27454     },
27455
27456     /**
27457      * Protected method that will not generally be called directly. It
27458      * is called when the editor creates its toolbar. Override this method if you need to
27459      * add custom toolbar buttons.
27460      * @param {HtmlEditor} editor
27461      */
27462     createToolbar : function(){
27463         Roo.log('renewing');
27464         Roo.log("create toolbars");
27465         
27466         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27467         this.toolbars[0].render(this.toolbarContainer());
27468         
27469         return;
27470         
27471 //        if (!editor.toolbars || !editor.toolbars.length) {
27472 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27473 //        }
27474 //        
27475 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27476 //            editor.toolbars[i] = Roo.factory(
27477 //                    typeof(editor.toolbars[i]) == 'string' ?
27478 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27479 //                Roo.bootstrap.HtmlEditor);
27480 //            editor.toolbars[i].init(editor);
27481 //        }
27482     },
27483
27484      
27485     // private
27486     onRender : function(ct, position)
27487     {
27488        // Roo.log("Call onRender: " + this.xtype);
27489         var _t = this;
27490         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27491       
27492         this.wrap = this.inputEl().wrap({
27493             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27494         });
27495         
27496         this.editorcore.onRender(ct, position);
27497          
27498         if (this.resizable) {
27499             this.resizeEl = new Roo.Resizable(this.wrap, {
27500                 pinned : true,
27501                 wrap: true,
27502                 dynamic : true,
27503                 minHeight : this.height,
27504                 height: this.height,
27505                 handles : this.resizable,
27506                 width: this.width,
27507                 listeners : {
27508                     resize : function(r, w, h) {
27509                         _t.onResize(w,h); // -something
27510                     }
27511                 }
27512             });
27513             
27514         }
27515         this.createToolbar(this);
27516        
27517         
27518         if(!this.width && this.resizable){
27519             this.setSize(this.wrap.getSize());
27520         }
27521         if (this.resizeEl) {
27522             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27523             // should trigger onReize..
27524         }
27525         
27526     },
27527
27528     // private
27529     onResize : function(w, h)
27530     {
27531         Roo.log('resize: ' +w + ',' + h );
27532         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27533         var ew = false;
27534         var eh = false;
27535         
27536         if(this.inputEl() ){
27537             if(typeof w == 'number'){
27538                 var aw = w - this.wrap.getFrameWidth('lr');
27539                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27540                 ew = aw;
27541             }
27542             if(typeof h == 'number'){
27543                  var tbh = -11;  // fixme it needs to tool bar size!
27544                 for (var i =0; i < this.toolbars.length;i++) {
27545                     // fixme - ask toolbars for heights?
27546                     tbh += this.toolbars[i].el.getHeight();
27547                     //if (this.toolbars[i].footer) {
27548                     //    tbh += this.toolbars[i].footer.el.getHeight();
27549                     //}
27550                 }
27551               
27552                 
27553                 
27554                 
27555                 
27556                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27557                 ah -= 5; // knock a few pixes off for look..
27558                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27559                 var eh = ah;
27560             }
27561         }
27562         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27563         this.editorcore.onResize(ew,eh);
27564         
27565     },
27566
27567     /**
27568      * Toggles the editor between standard and source edit mode.
27569      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27570      */
27571     toggleSourceEdit : function(sourceEditMode)
27572     {
27573         this.editorcore.toggleSourceEdit(sourceEditMode);
27574         
27575         if(this.editorcore.sourceEditMode){
27576             Roo.log('editor - showing textarea');
27577             
27578 //            Roo.log('in');
27579 //            Roo.log(this.syncValue());
27580             this.syncValue();
27581             this.inputEl().removeClass(['hide', 'x-hidden']);
27582             this.inputEl().dom.removeAttribute('tabIndex');
27583             this.inputEl().focus();
27584         }else{
27585             Roo.log('editor - hiding textarea');
27586 //            Roo.log('out')
27587 //            Roo.log(this.pushValue()); 
27588             this.pushValue();
27589             
27590             this.inputEl().addClass(['hide', 'x-hidden']);
27591             this.inputEl().dom.setAttribute('tabIndex', -1);
27592             //this.deferFocus();
27593         }
27594          
27595         if(this.resizable){
27596             this.setSize(this.wrap.getSize());
27597         }
27598         
27599         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27600     },
27601  
27602     // private (for BoxComponent)
27603     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27604
27605     // private (for BoxComponent)
27606     getResizeEl : function(){
27607         return this.wrap;
27608     },
27609
27610     // private (for BoxComponent)
27611     getPositionEl : function(){
27612         return this.wrap;
27613     },
27614
27615     // private
27616     initEvents : function(){
27617         this.originalValue = this.getValue();
27618     },
27619
27620 //    /**
27621 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27622 //     * @method
27623 //     */
27624 //    markInvalid : Roo.emptyFn,
27625 //    /**
27626 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27627 //     * @method
27628 //     */
27629 //    clearInvalid : Roo.emptyFn,
27630
27631     setValue : function(v){
27632         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27633         this.editorcore.pushValue();
27634     },
27635
27636      
27637     // private
27638     deferFocus : function(){
27639         this.focus.defer(10, this);
27640     },
27641
27642     // doc'ed in Field
27643     focus : function(){
27644         this.editorcore.focus();
27645         
27646     },
27647       
27648
27649     // private
27650     onDestroy : function(){
27651         
27652         
27653         
27654         if(this.rendered){
27655             
27656             for (var i =0; i < this.toolbars.length;i++) {
27657                 // fixme - ask toolbars for heights?
27658                 this.toolbars[i].onDestroy();
27659             }
27660             
27661             this.wrap.dom.innerHTML = '';
27662             this.wrap.remove();
27663         }
27664     },
27665
27666     // private
27667     onFirstFocus : function(){
27668         //Roo.log("onFirstFocus");
27669         this.editorcore.onFirstFocus();
27670          for (var i =0; i < this.toolbars.length;i++) {
27671             this.toolbars[i].onFirstFocus();
27672         }
27673         
27674     },
27675     
27676     // private
27677     syncValue : function()
27678     {   
27679         this.editorcore.syncValue();
27680     },
27681     
27682     pushValue : function()
27683     {   
27684         this.editorcore.pushValue();
27685     }
27686      
27687     
27688     // hide stuff that is not compatible
27689     /**
27690      * @event blur
27691      * @hide
27692      */
27693     /**
27694      * @event change
27695      * @hide
27696      */
27697     /**
27698      * @event focus
27699      * @hide
27700      */
27701     /**
27702      * @event specialkey
27703      * @hide
27704      */
27705     /**
27706      * @cfg {String} fieldClass @hide
27707      */
27708     /**
27709      * @cfg {String} focusClass @hide
27710      */
27711     /**
27712      * @cfg {String} autoCreate @hide
27713      */
27714     /**
27715      * @cfg {String} inputType @hide
27716      */
27717      
27718     /**
27719      * @cfg {String} invalidText @hide
27720      */
27721     /**
27722      * @cfg {String} msgFx @hide
27723      */
27724     /**
27725      * @cfg {String} validateOnBlur @hide
27726      */
27727 });
27728  
27729     
27730    
27731    
27732    
27733       
27734 Roo.namespace('Roo.bootstrap.htmleditor');
27735 /**
27736  * @class Roo.bootstrap.HtmlEditorToolbar1
27737  * Basic Toolbar
27738  * 
27739  * @example
27740  * Usage:
27741  *
27742  new Roo.bootstrap.HtmlEditor({
27743     ....
27744     toolbars : [
27745         new Roo.bootstrap.HtmlEditorToolbar1({
27746             disable : { fonts: 1 , format: 1, ..., ... , ...],
27747             btns : [ .... ]
27748         })
27749     }
27750      
27751  * 
27752  * @cfg {Object} disable List of elements to disable..
27753  * @cfg {Array} btns List of additional buttons.
27754  * 
27755  * 
27756  * NEEDS Extra CSS? 
27757  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27758  */
27759  
27760 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27761 {
27762     
27763     Roo.apply(this, config);
27764     
27765     // default disabled, based on 'good practice'..
27766     this.disable = this.disable || {};
27767     Roo.applyIf(this.disable, {
27768         fontSize : true,
27769         colors : true,
27770         specialElements : true
27771     });
27772     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27773     
27774     this.editor = config.editor;
27775     this.editorcore = config.editor.editorcore;
27776     
27777     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27778     
27779     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27780     // dont call parent... till later.
27781 }
27782 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27783      
27784     bar : true,
27785     
27786     editor : false,
27787     editorcore : false,
27788     
27789     
27790     formats : [
27791         "p" ,  
27792         "h1","h2","h3","h4","h5","h6", 
27793         "pre", "code", 
27794         "abbr", "acronym", "address", "cite", "samp", "var",
27795         'div','span'
27796     ],
27797     
27798     onRender : function(ct, position)
27799     {
27800        // Roo.log("Call onRender: " + this.xtype);
27801         
27802        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27803        Roo.log(this.el);
27804        this.el.dom.style.marginBottom = '0';
27805        var _this = this;
27806        var editorcore = this.editorcore;
27807        var editor= this.editor;
27808        
27809        var children = [];
27810        var btn = function(id,cmd , toggle, handler, html){
27811        
27812             var  event = toggle ? 'toggle' : 'click';
27813        
27814             var a = {
27815                 size : 'sm',
27816                 xtype: 'Button',
27817                 xns: Roo.bootstrap,
27818                 //glyphicon : id,
27819                 fa: id,
27820                 cmd : id || cmd,
27821                 enableToggle:toggle !== false,
27822                 html : html || '',
27823                 pressed : toggle ? false : null,
27824                 listeners : {}
27825             };
27826             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27827                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27828             };
27829             children.push(a);
27830             return a;
27831        }
27832        
27833     //    var cb_box = function...
27834         
27835         var style = {
27836                 xtype: 'Button',
27837                 size : 'sm',
27838                 xns: Roo.bootstrap,
27839                 fa : 'font',
27840                 //html : 'submit'
27841                 menu : {
27842                     xtype: 'Menu',
27843                     xns: Roo.bootstrap,
27844                     items:  []
27845                 }
27846         };
27847         Roo.each(this.formats, function(f) {
27848             style.menu.items.push({
27849                 xtype :'MenuItem',
27850                 xns: Roo.bootstrap,
27851                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27852                 tagname : f,
27853                 listeners : {
27854                     click : function()
27855                     {
27856                         editorcore.insertTag(this.tagname);
27857                         editor.focus();
27858                     }
27859                 }
27860                 
27861             });
27862         });
27863         children.push(style);   
27864         
27865         btn('bold',false,true);
27866         btn('italic',false,true);
27867         btn('align-left', 'justifyleft',true);
27868         btn('align-center', 'justifycenter',true);
27869         btn('align-right' , 'justifyright',true);
27870         btn('link', false, false, function(btn) {
27871             //Roo.log("create link?");
27872             var url = prompt(this.createLinkText, this.defaultLinkValue);
27873             if(url && url != 'http:/'+'/'){
27874                 this.editorcore.relayCmd('createlink', url);
27875             }
27876         }),
27877         btn('list','insertunorderedlist',true);
27878         btn('pencil', false,true, function(btn){
27879                 Roo.log(this);
27880                 this.toggleSourceEdit(btn.pressed);
27881         });
27882         
27883         if (this.editor.btns.length > 0) {
27884             for (var i = 0; i<this.editor.btns.length; i++) {
27885                 children.push(this.editor.btns[i]);
27886             }
27887         }
27888         
27889         /*
27890         var cog = {
27891                 xtype: 'Button',
27892                 size : 'sm',
27893                 xns: Roo.bootstrap,
27894                 glyphicon : 'cog',
27895                 //html : 'submit'
27896                 menu : {
27897                     xtype: 'Menu',
27898                     xns: Roo.bootstrap,
27899                     items:  []
27900                 }
27901         };
27902         
27903         cog.menu.items.push({
27904             xtype :'MenuItem',
27905             xns: Roo.bootstrap,
27906             html : Clean styles,
27907             tagname : f,
27908             listeners : {
27909                 click : function()
27910                 {
27911                     editorcore.insertTag(this.tagname);
27912                     editor.focus();
27913                 }
27914             }
27915             
27916         });
27917        */
27918         
27919          
27920        this.xtype = 'NavSimplebar';
27921         
27922         for(var i=0;i< children.length;i++) {
27923             
27924             this.buttons.add(this.addxtypeChild(children[i]));
27925             
27926         }
27927         
27928         editor.on('editorevent', this.updateToolbar, this);
27929     },
27930     onBtnClick : function(id)
27931     {
27932        this.editorcore.relayCmd(id);
27933        this.editorcore.focus();
27934     },
27935     
27936     /**
27937      * Protected method that will not generally be called directly. It triggers
27938      * a toolbar update by reading the markup state of the current selection in the editor.
27939      */
27940     updateToolbar: function(){
27941
27942         if(!this.editorcore.activated){
27943             this.editor.onFirstFocus(); // is this neeed?
27944             return;
27945         }
27946
27947         var btns = this.buttons; 
27948         var doc = this.editorcore.doc;
27949         btns.get('bold').setActive(doc.queryCommandState('bold'));
27950         btns.get('italic').setActive(doc.queryCommandState('italic'));
27951         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27952         
27953         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27954         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27955         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27956         
27957         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27958         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27959          /*
27960         
27961         var ans = this.editorcore.getAllAncestors();
27962         if (this.formatCombo) {
27963             
27964             
27965             var store = this.formatCombo.store;
27966             this.formatCombo.setValue("");
27967             for (var i =0; i < ans.length;i++) {
27968                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27969                     // select it..
27970                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27971                     break;
27972                 }
27973             }
27974         }
27975         
27976         
27977         
27978         // hides menus... - so this cant be on a menu...
27979         Roo.bootstrap.MenuMgr.hideAll();
27980         */
27981         Roo.bootstrap.menu.Manager.hideAll();
27982         //this.editorsyncValue();
27983     },
27984     onFirstFocus: function() {
27985         this.buttons.each(function(item){
27986            item.enable();
27987         });
27988     },
27989     toggleSourceEdit : function(sourceEditMode){
27990         
27991           
27992         if(sourceEditMode){
27993             Roo.log("disabling buttons");
27994            this.buttons.each( function(item){
27995                 if(item.cmd != 'pencil'){
27996                     item.disable();
27997                 }
27998             });
27999           
28000         }else{
28001             Roo.log("enabling buttons");
28002             if(this.editorcore.initialized){
28003                 this.buttons.each( function(item){
28004                     item.enable();
28005                 });
28006             }
28007             
28008         }
28009         Roo.log("calling toggole on editor");
28010         // tell the editor that it's been pressed..
28011         this.editor.toggleSourceEdit(sourceEditMode);
28012        
28013     }
28014 });
28015
28016
28017
28018
28019  
28020 /*
28021  * - LGPL
28022  */
28023
28024 /**
28025  * @class Roo.bootstrap.Markdown
28026  * @extends Roo.bootstrap.TextArea
28027  * Bootstrap Showdown editable area
28028  * @cfg {string} content
28029  * 
28030  * @constructor
28031  * Create a new Showdown
28032  */
28033
28034 Roo.bootstrap.Markdown = function(config){
28035     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28036    
28037 };
28038
28039 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28040     
28041     editing :false,
28042     
28043     initEvents : function()
28044     {
28045         
28046         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28047         this.markdownEl = this.el.createChild({
28048             cls : 'roo-markdown-area'
28049         });
28050         this.inputEl().addClass('d-none');
28051         if (this.getValue() == '') {
28052             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28053             
28054         } else {
28055             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28056         }
28057         this.markdownEl.on('click', this.toggleTextEdit, this);
28058         this.on('blur', this.toggleTextEdit, this);
28059         this.on('specialkey', this.resizeTextArea, this);
28060     },
28061     
28062     toggleTextEdit : function()
28063     {
28064         var sh = this.markdownEl.getHeight();
28065         this.inputEl().addClass('d-none');
28066         this.markdownEl.addClass('d-none');
28067         if (!this.editing) {
28068             // show editor?
28069             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28070             this.inputEl().removeClass('d-none');
28071             this.inputEl().focus();
28072             this.editing = true;
28073             return;
28074         }
28075         // show showdown...
28076         this.updateMarkdown();
28077         this.markdownEl.removeClass('d-none');
28078         this.editing = false;
28079         return;
28080     },
28081     updateMarkdown : function()
28082     {
28083         if (this.getValue() == '') {
28084             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28085             return;
28086         }
28087  
28088         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28089     },
28090     
28091     resizeTextArea: function () {
28092         
28093         var sh = 100;
28094         Roo.log([sh, this.getValue().split("\n").length * 30]);
28095         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28096     },
28097     setValue : function(val)
28098     {
28099         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28100         if (!this.editing) {
28101             this.updateMarkdown();
28102         }
28103         
28104     },
28105     focus : function()
28106     {
28107         if (!this.editing) {
28108             this.toggleTextEdit();
28109         }
28110         
28111     }
28112
28113
28114 });/*
28115  * Based on:
28116  * Ext JS Library 1.1.1
28117  * Copyright(c) 2006-2007, Ext JS, LLC.
28118  *
28119  * Originally Released Under LGPL - original licence link has changed is not relivant.
28120  *
28121  * Fork - LGPL
28122  * <script type="text/javascript">
28123  */
28124  
28125 /**
28126  * @class Roo.bootstrap.PagingToolbar
28127  * @extends Roo.bootstrap.NavSimplebar
28128  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28129  * @constructor
28130  * Create a new PagingToolbar
28131  * @param {Object} config The config object
28132  * @param {Roo.data.Store} store
28133  */
28134 Roo.bootstrap.PagingToolbar = function(config)
28135 {
28136     // old args format still supported... - xtype is prefered..
28137         // created from xtype...
28138     
28139     this.ds = config.dataSource;
28140     
28141     if (config.store && !this.ds) {
28142         this.store= Roo.factory(config.store, Roo.data);
28143         this.ds = this.store;
28144         this.ds.xmodule = this.xmodule || false;
28145     }
28146     
28147     this.toolbarItems = [];
28148     if (config.items) {
28149         this.toolbarItems = config.items;
28150     }
28151     
28152     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28153     
28154     this.cursor = 0;
28155     
28156     if (this.ds) { 
28157         this.bind(this.ds);
28158     }
28159     
28160     if (Roo.bootstrap.version == 4) {
28161         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28162     } else {
28163         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28164     }
28165     
28166 };
28167
28168 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28169     /**
28170      * @cfg {Roo.bootstrap.Button} buttons[]
28171      * Buttons for the toolbar
28172      */
28173      /**
28174      * @cfg {Roo.data.Store} store
28175      * The underlying data store providing the paged data
28176      */
28177     /**
28178      * @cfg {String/HTMLElement/Element} container
28179      * container The id or element that will contain the toolbar
28180      */
28181     /**
28182      * @cfg {Boolean} displayInfo
28183      * True to display the displayMsg (defaults to false)
28184      */
28185     /**
28186      * @cfg {Number} pageSize
28187      * The number of records to display per page (defaults to 20)
28188      */
28189     pageSize: 20,
28190     /**
28191      * @cfg {String} displayMsg
28192      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28193      */
28194     displayMsg : 'Displaying {0} - {1} of {2}',
28195     /**
28196      * @cfg {String} emptyMsg
28197      * The message to display when no records are found (defaults to "No data to display")
28198      */
28199     emptyMsg : 'No data to display',
28200     /**
28201      * Customizable piece of the default paging text (defaults to "Page")
28202      * @type String
28203      */
28204     beforePageText : "Page",
28205     /**
28206      * Customizable piece of the default paging text (defaults to "of %0")
28207      * @type String
28208      */
28209     afterPageText : "of {0}",
28210     /**
28211      * Customizable piece of the default paging text (defaults to "First Page")
28212      * @type String
28213      */
28214     firstText : "First Page",
28215     /**
28216      * Customizable piece of the default paging text (defaults to "Previous Page")
28217      * @type String
28218      */
28219     prevText : "Previous Page",
28220     /**
28221      * Customizable piece of the default paging text (defaults to "Next Page")
28222      * @type String
28223      */
28224     nextText : "Next Page",
28225     /**
28226      * Customizable piece of the default paging text (defaults to "Last Page")
28227      * @type String
28228      */
28229     lastText : "Last Page",
28230     /**
28231      * Customizable piece of the default paging text (defaults to "Refresh")
28232      * @type String
28233      */
28234     refreshText : "Refresh",
28235
28236     buttons : false,
28237     // private
28238     onRender : function(ct, position) 
28239     {
28240         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28241         this.navgroup.parentId = this.id;
28242         this.navgroup.onRender(this.el, null);
28243         // add the buttons to the navgroup
28244         
28245         if(this.displayInfo){
28246             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28247             this.displayEl = this.el.select('.x-paging-info', true).first();
28248 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28249 //            this.displayEl = navel.el.select('span',true).first();
28250         }
28251         
28252         var _this = this;
28253         
28254         if(this.buttons){
28255             Roo.each(_this.buttons, function(e){ // this might need to use render????
28256                Roo.factory(e).render(_this.el);
28257             });
28258         }
28259             
28260         Roo.each(_this.toolbarItems, function(e) {
28261             _this.navgroup.addItem(e);
28262         });
28263         
28264         
28265         this.first = this.navgroup.addItem({
28266             tooltip: this.firstText,
28267             cls: "prev btn-outline-secondary",
28268             html : ' <i class="fa fa-step-backward"></i>',
28269             disabled: true,
28270             preventDefault: true,
28271             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28272         });
28273         
28274         this.prev =  this.navgroup.addItem({
28275             tooltip: this.prevText,
28276             cls: "prev btn-outline-secondary",
28277             html : ' <i class="fa fa-backward"></i>',
28278             disabled: true,
28279             preventDefault: true,
28280             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28281         });
28282     //this.addSeparator();
28283         
28284         
28285         var field = this.navgroup.addItem( {
28286             tagtype : 'span',
28287             cls : 'x-paging-position  btn-outline-secondary',
28288              disabled: true,
28289             html : this.beforePageText  +
28290                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28291                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28292          } ); //?? escaped?
28293         
28294         this.field = field.el.select('input', true).first();
28295         this.field.on("keydown", this.onPagingKeydown, this);
28296         this.field.on("focus", function(){this.dom.select();});
28297     
28298     
28299         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28300         //this.field.setHeight(18);
28301         //this.addSeparator();
28302         this.next = this.navgroup.addItem({
28303             tooltip: this.nextText,
28304             cls: "next btn-outline-secondary",
28305             html : ' <i class="fa fa-forward"></i>',
28306             disabled: true,
28307             preventDefault: true,
28308             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28309         });
28310         this.last = this.navgroup.addItem({
28311             tooltip: this.lastText,
28312             html : ' <i class="fa fa-step-forward"></i>',
28313             cls: "next btn-outline-secondary",
28314             disabled: true,
28315             preventDefault: true,
28316             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28317         });
28318     //this.addSeparator();
28319         this.loading = this.navgroup.addItem({
28320             tooltip: this.refreshText,
28321             cls: "btn-outline-secondary",
28322             html : ' <i class="fa fa-refresh"></i>',
28323             preventDefault: true,
28324             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28325         });
28326         
28327     },
28328
28329     // private
28330     updateInfo : function(){
28331         if(this.displayEl){
28332             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28333             var msg = count == 0 ?
28334                 this.emptyMsg :
28335                 String.format(
28336                     this.displayMsg,
28337                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28338                 );
28339             this.displayEl.update(msg);
28340         }
28341     },
28342
28343     // private
28344     onLoad : function(ds, r, o)
28345     {
28346         this.cursor = o.params && o.params.start ? o.params.start : 0;
28347         
28348         var d = this.getPageData(),
28349             ap = d.activePage,
28350             ps = d.pages;
28351         
28352         
28353         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28354         this.field.dom.value = ap;
28355         this.first.setDisabled(ap == 1);
28356         this.prev.setDisabled(ap == 1);
28357         this.next.setDisabled(ap == ps);
28358         this.last.setDisabled(ap == ps);
28359         this.loading.enable();
28360         this.updateInfo();
28361     },
28362
28363     // private
28364     getPageData : function(){
28365         var total = this.ds.getTotalCount();
28366         return {
28367             total : total,
28368             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28369             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28370         };
28371     },
28372
28373     // private
28374     onLoadError : function(){
28375         this.loading.enable();
28376     },
28377
28378     // private
28379     onPagingKeydown : function(e){
28380         var k = e.getKey();
28381         var d = this.getPageData();
28382         if(k == e.RETURN){
28383             var v = this.field.dom.value, pageNum;
28384             if(!v || isNaN(pageNum = parseInt(v, 10))){
28385                 this.field.dom.value = d.activePage;
28386                 return;
28387             }
28388             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28389             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28390             e.stopEvent();
28391         }
28392         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))
28393         {
28394           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28395           this.field.dom.value = pageNum;
28396           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28397           e.stopEvent();
28398         }
28399         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28400         {
28401           var v = this.field.dom.value, pageNum; 
28402           var increment = (e.shiftKey) ? 10 : 1;
28403           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28404                 increment *= -1;
28405           }
28406           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28407             this.field.dom.value = d.activePage;
28408             return;
28409           }
28410           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28411           {
28412             this.field.dom.value = parseInt(v, 10) + increment;
28413             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28414             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28415           }
28416           e.stopEvent();
28417         }
28418     },
28419
28420     // private
28421     beforeLoad : function(){
28422         if(this.loading){
28423             this.loading.disable();
28424         }
28425     },
28426
28427     // private
28428     onClick : function(which){
28429         
28430         var ds = this.ds;
28431         if (!ds) {
28432             return;
28433         }
28434         
28435         switch(which){
28436             case "first":
28437                 ds.load({params:{start: 0, limit: this.pageSize}});
28438             break;
28439             case "prev":
28440                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28441             break;
28442             case "next":
28443                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28444             break;
28445             case "last":
28446                 var total = ds.getTotalCount();
28447                 var extra = total % this.pageSize;
28448                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28449                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28450             break;
28451             case "refresh":
28452                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28453             break;
28454         }
28455     },
28456
28457     /**
28458      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28459      * @param {Roo.data.Store} store The data store to unbind
28460      */
28461     unbind : function(ds){
28462         ds.un("beforeload", this.beforeLoad, this);
28463         ds.un("load", this.onLoad, this);
28464         ds.un("loadexception", this.onLoadError, this);
28465         ds.un("remove", this.updateInfo, this);
28466         ds.un("add", this.updateInfo, this);
28467         this.ds = undefined;
28468     },
28469
28470     /**
28471      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28472      * @param {Roo.data.Store} store The data store to bind
28473      */
28474     bind : function(ds){
28475         ds.on("beforeload", this.beforeLoad, this);
28476         ds.on("load", this.onLoad, this);
28477         ds.on("loadexception", this.onLoadError, this);
28478         ds.on("remove", this.updateInfo, this);
28479         ds.on("add", this.updateInfo, this);
28480         this.ds = ds;
28481     }
28482 });/*
28483  * - LGPL
28484  *
28485  * element
28486  * 
28487  */
28488
28489 /**
28490  * @class Roo.bootstrap.MessageBar
28491  * @extends Roo.bootstrap.Component
28492  * Bootstrap MessageBar class
28493  * @cfg {String} html contents of the MessageBar
28494  * @cfg {String} weight (info | success | warning | danger) default info
28495  * @cfg {String} beforeClass insert the bar before the given class
28496  * @cfg {Boolean} closable (true | false) default false
28497  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28498  * 
28499  * @constructor
28500  * Create a new Element
28501  * @param {Object} config The config object
28502  */
28503
28504 Roo.bootstrap.MessageBar = function(config){
28505     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28506 };
28507
28508 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28509     
28510     html: '',
28511     weight: 'info',
28512     closable: false,
28513     fixed: false,
28514     beforeClass: 'bootstrap-sticky-wrap',
28515     
28516     getAutoCreate : function(){
28517         
28518         var cfg = {
28519             tag: 'div',
28520             cls: 'alert alert-dismissable alert-' + this.weight,
28521             cn: [
28522                 {
28523                     tag: 'span',
28524                     cls: 'message',
28525                     html: this.html || ''
28526                 }
28527             ]
28528         };
28529         
28530         if(this.fixed){
28531             cfg.cls += ' alert-messages-fixed';
28532         }
28533         
28534         if(this.closable){
28535             cfg.cn.push({
28536                 tag: 'button',
28537                 cls: 'close',
28538                 html: 'x'
28539             });
28540         }
28541         
28542         return cfg;
28543     },
28544     
28545     onRender : function(ct, position)
28546     {
28547         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28548         
28549         if(!this.el){
28550             var cfg = Roo.apply({},  this.getAutoCreate());
28551             cfg.id = Roo.id();
28552             
28553             if (this.cls) {
28554                 cfg.cls += ' ' + this.cls;
28555             }
28556             if (this.style) {
28557                 cfg.style = this.style;
28558             }
28559             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28560             
28561             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28562         }
28563         
28564         this.el.select('>button.close').on('click', this.hide, this);
28565         
28566     },
28567     
28568     show : function()
28569     {
28570         if (!this.rendered) {
28571             this.render();
28572         }
28573         
28574         this.el.show();
28575         
28576         this.fireEvent('show', this);
28577         
28578     },
28579     
28580     hide : function()
28581     {
28582         if (!this.rendered) {
28583             this.render();
28584         }
28585         
28586         this.el.hide();
28587         
28588         this.fireEvent('hide', this);
28589     },
28590     
28591     update : function()
28592     {
28593 //        var e = this.el.dom.firstChild;
28594 //        
28595 //        if(this.closable){
28596 //            e = e.nextSibling;
28597 //        }
28598 //        
28599 //        e.data = this.html || '';
28600
28601         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28602     }
28603    
28604 });
28605
28606  
28607
28608      /*
28609  * - LGPL
28610  *
28611  * Graph
28612  * 
28613  */
28614
28615
28616 /**
28617  * @class Roo.bootstrap.Graph
28618  * @extends Roo.bootstrap.Component
28619  * Bootstrap Graph class
28620 > Prameters
28621  -sm {number} sm 4
28622  -md {number} md 5
28623  @cfg {String} graphtype  bar | vbar | pie
28624  @cfg {number} g_x coodinator | centre x (pie)
28625  @cfg {number} g_y coodinator | centre y (pie)
28626  @cfg {number} g_r radius (pie)
28627  @cfg {number} g_height height of the chart (respected by all elements in the set)
28628  @cfg {number} g_width width of the chart (respected by all elements in the set)
28629  @cfg {Object} title The title of the chart
28630     
28631  -{Array}  values
28632  -opts (object) options for the chart 
28633      o {
28634      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28635      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28636      o vgutter (number)
28637      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.
28638      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28639      o to
28640      o stretch (boolean)
28641      o }
28642  -opts (object) options for the pie
28643      o{
28644      o cut
28645      o startAngle (number)
28646      o endAngle (number)
28647      } 
28648  *
28649  * @constructor
28650  * Create a new Input
28651  * @param {Object} config The config object
28652  */
28653
28654 Roo.bootstrap.Graph = function(config){
28655     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28656     
28657     this.addEvents({
28658         // img events
28659         /**
28660          * @event click
28661          * The img click event for the img.
28662          * @param {Roo.EventObject} e
28663          */
28664         "click" : true
28665     });
28666 };
28667
28668 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28669     
28670     sm: 4,
28671     md: 5,
28672     graphtype: 'bar',
28673     g_height: 250,
28674     g_width: 400,
28675     g_x: 50,
28676     g_y: 50,
28677     g_r: 30,
28678     opts:{
28679         //g_colors: this.colors,
28680         g_type: 'soft',
28681         g_gutter: '20%'
28682
28683     },
28684     title : false,
28685
28686     getAutoCreate : function(){
28687         
28688         var cfg = {
28689             tag: 'div',
28690             html : null
28691         };
28692         
28693         
28694         return  cfg;
28695     },
28696
28697     onRender : function(ct,position){
28698         
28699         
28700         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28701         
28702         if (typeof(Raphael) == 'undefined') {
28703             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28704             return;
28705         }
28706         
28707         this.raphael = Raphael(this.el.dom);
28708         
28709                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28710                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28711                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28712                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28713                 /*
28714                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28715                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28716                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28717                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28718                 
28719                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28720                 r.barchart(330, 10, 300, 220, data1);
28721                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28722                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28723                 */
28724                 
28725                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28726                 // r.barchart(30, 30, 560, 250,  xdata, {
28727                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28728                 //     axis : "0 0 1 1",
28729                 //     axisxlabels :  xdata
28730                 //     //yvalues : cols,
28731                    
28732                 // });
28733 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28734 //        
28735 //        this.load(null,xdata,{
28736 //                axis : "0 0 1 1",
28737 //                axisxlabels :  xdata
28738 //                });
28739
28740     },
28741
28742     load : function(graphtype,xdata,opts)
28743     {
28744         this.raphael.clear();
28745         if(!graphtype) {
28746             graphtype = this.graphtype;
28747         }
28748         if(!opts){
28749             opts = this.opts;
28750         }
28751         var r = this.raphael,
28752             fin = function () {
28753                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28754             },
28755             fout = function () {
28756                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28757             },
28758             pfin = function() {
28759                 this.sector.stop();
28760                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28761
28762                 if (this.label) {
28763                     this.label[0].stop();
28764                     this.label[0].attr({ r: 7.5 });
28765                     this.label[1].attr({ "font-weight": 800 });
28766                 }
28767             },
28768             pfout = function() {
28769                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28770
28771                 if (this.label) {
28772                     this.label[0].animate({ r: 5 }, 500, "bounce");
28773                     this.label[1].attr({ "font-weight": 400 });
28774                 }
28775             };
28776
28777         switch(graphtype){
28778             case 'bar':
28779                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28780                 break;
28781             case 'hbar':
28782                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28783                 break;
28784             case 'pie':
28785 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28786 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28787 //            
28788                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28789                 
28790                 break;
28791
28792         }
28793         
28794         if(this.title){
28795             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28796         }
28797         
28798     },
28799     
28800     setTitle: function(o)
28801     {
28802         this.title = o;
28803     },
28804     
28805     initEvents: function() {
28806         
28807         if(!this.href){
28808             this.el.on('click', this.onClick, this);
28809         }
28810     },
28811     
28812     onClick : function(e)
28813     {
28814         Roo.log('img onclick');
28815         this.fireEvent('click', this, e);
28816     }
28817    
28818 });
28819
28820  
28821 /*
28822  * - LGPL
28823  *
28824  * numberBox
28825  * 
28826  */
28827 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28828
28829 /**
28830  * @class Roo.bootstrap.dash.NumberBox
28831  * @extends Roo.bootstrap.Component
28832  * Bootstrap NumberBox class
28833  * @cfg {String} headline Box headline
28834  * @cfg {String} content Box content
28835  * @cfg {String} icon Box icon
28836  * @cfg {String} footer Footer text
28837  * @cfg {String} fhref Footer href
28838  * 
28839  * @constructor
28840  * Create a new NumberBox
28841  * @param {Object} config The config object
28842  */
28843
28844
28845 Roo.bootstrap.dash.NumberBox = function(config){
28846     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28847     
28848 };
28849
28850 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28851     
28852     headline : '',
28853     content : '',
28854     icon : '',
28855     footer : '',
28856     fhref : '',
28857     ficon : '',
28858     
28859     getAutoCreate : function(){
28860         
28861         var cfg = {
28862             tag : 'div',
28863             cls : 'small-box ',
28864             cn : [
28865                 {
28866                     tag : 'div',
28867                     cls : 'inner',
28868                     cn :[
28869                         {
28870                             tag : 'h3',
28871                             cls : 'roo-headline',
28872                             html : this.headline
28873                         },
28874                         {
28875                             tag : 'p',
28876                             cls : 'roo-content',
28877                             html : this.content
28878                         }
28879                     ]
28880                 }
28881             ]
28882         };
28883         
28884         if(this.icon){
28885             cfg.cn.push({
28886                 tag : 'div',
28887                 cls : 'icon',
28888                 cn :[
28889                     {
28890                         tag : 'i',
28891                         cls : 'ion ' + this.icon
28892                     }
28893                 ]
28894             });
28895         }
28896         
28897         if(this.footer){
28898             var footer = {
28899                 tag : 'a',
28900                 cls : 'small-box-footer',
28901                 href : this.fhref || '#',
28902                 html : this.footer
28903             };
28904             
28905             cfg.cn.push(footer);
28906             
28907         }
28908         
28909         return  cfg;
28910     },
28911
28912     onRender : function(ct,position){
28913         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28914
28915
28916        
28917                 
28918     },
28919
28920     setHeadline: function (value)
28921     {
28922         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28923     },
28924     
28925     setFooter: function (value, href)
28926     {
28927         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28928         
28929         if(href){
28930             this.el.select('a.small-box-footer',true).first().attr('href', href);
28931         }
28932         
28933     },
28934
28935     setContent: function (value)
28936     {
28937         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28938     },
28939
28940     initEvents: function() 
28941     {   
28942         
28943     }
28944     
28945 });
28946
28947  
28948 /*
28949  * - LGPL
28950  *
28951  * TabBox
28952  * 
28953  */
28954 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28955
28956 /**
28957  * @class Roo.bootstrap.dash.TabBox
28958  * @extends Roo.bootstrap.Component
28959  * Bootstrap TabBox class
28960  * @cfg {String} title Title of the TabBox
28961  * @cfg {String} icon Icon of the TabBox
28962  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28963  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28964  * 
28965  * @constructor
28966  * Create a new TabBox
28967  * @param {Object} config The config object
28968  */
28969
28970
28971 Roo.bootstrap.dash.TabBox = function(config){
28972     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28973     this.addEvents({
28974         // raw events
28975         /**
28976          * @event addpane
28977          * When a pane is added
28978          * @param {Roo.bootstrap.dash.TabPane} pane
28979          */
28980         "addpane" : true,
28981         /**
28982          * @event activatepane
28983          * When a pane is activated
28984          * @param {Roo.bootstrap.dash.TabPane} pane
28985          */
28986         "activatepane" : true
28987         
28988          
28989     });
28990     
28991     this.panes = [];
28992 };
28993
28994 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28995
28996     title : '',
28997     icon : false,
28998     showtabs : true,
28999     tabScrollable : false,
29000     
29001     getChildContainer : function()
29002     {
29003         return this.el.select('.tab-content', true).first();
29004     },
29005     
29006     getAutoCreate : function(){
29007         
29008         var header = {
29009             tag: 'li',
29010             cls: 'pull-left header',
29011             html: this.title,
29012             cn : []
29013         };
29014         
29015         if(this.icon){
29016             header.cn.push({
29017                 tag: 'i',
29018                 cls: 'fa ' + this.icon
29019             });
29020         }
29021         
29022         var h = {
29023             tag: 'ul',
29024             cls: 'nav nav-tabs pull-right',
29025             cn: [
29026                 header
29027             ]
29028         };
29029         
29030         if(this.tabScrollable){
29031             h = {
29032                 tag: 'div',
29033                 cls: 'tab-header',
29034                 cn: [
29035                     {
29036                         tag: 'ul',
29037                         cls: 'nav nav-tabs pull-right',
29038                         cn: [
29039                             header
29040                         ]
29041                     }
29042                 ]
29043             };
29044         }
29045         
29046         var cfg = {
29047             tag: 'div',
29048             cls: 'nav-tabs-custom',
29049             cn: [
29050                 h,
29051                 {
29052                     tag: 'div',
29053                     cls: 'tab-content no-padding',
29054                     cn: []
29055                 }
29056             ]
29057         };
29058
29059         return  cfg;
29060     },
29061     initEvents : function()
29062     {
29063         //Roo.log('add add pane handler');
29064         this.on('addpane', this.onAddPane, this);
29065     },
29066      /**
29067      * Updates the box title
29068      * @param {String} html to set the title to.
29069      */
29070     setTitle : function(value)
29071     {
29072         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29073     },
29074     onAddPane : function(pane)
29075     {
29076         this.panes.push(pane);
29077         //Roo.log('addpane');
29078         //Roo.log(pane);
29079         // tabs are rendere left to right..
29080         if(!this.showtabs){
29081             return;
29082         }
29083         
29084         var ctr = this.el.select('.nav-tabs', true).first();
29085          
29086          
29087         var existing = ctr.select('.nav-tab',true);
29088         var qty = existing.getCount();;
29089         
29090         
29091         var tab = ctr.createChild({
29092             tag : 'li',
29093             cls : 'nav-tab' + (qty ? '' : ' active'),
29094             cn : [
29095                 {
29096                     tag : 'a',
29097                     href:'#',
29098                     html : pane.title
29099                 }
29100             ]
29101         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29102         pane.tab = tab;
29103         
29104         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29105         if (!qty) {
29106             pane.el.addClass('active');
29107         }
29108         
29109                 
29110     },
29111     onTabClick : function(ev,un,ob,pane)
29112     {
29113         //Roo.log('tab - prev default');
29114         ev.preventDefault();
29115         
29116         
29117         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29118         pane.tab.addClass('active');
29119         //Roo.log(pane.title);
29120         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29121         // technically we should have a deactivate event.. but maybe add later.
29122         // and it should not de-activate the selected tab...
29123         this.fireEvent('activatepane', pane);
29124         pane.el.addClass('active');
29125         pane.fireEvent('activate');
29126         
29127         
29128     },
29129     
29130     getActivePane : function()
29131     {
29132         var r = false;
29133         Roo.each(this.panes, function(p) {
29134             if(p.el.hasClass('active')){
29135                 r = p;
29136                 return false;
29137             }
29138             
29139             return;
29140         });
29141         
29142         return r;
29143     }
29144     
29145     
29146 });
29147
29148  
29149 /*
29150  * - LGPL
29151  *
29152  * Tab pane
29153  * 
29154  */
29155 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29156 /**
29157  * @class Roo.bootstrap.TabPane
29158  * @extends Roo.bootstrap.Component
29159  * Bootstrap TabPane class
29160  * @cfg {Boolean} active (false | true) Default false
29161  * @cfg {String} title title of panel
29162
29163  * 
29164  * @constructor
29165  * Create a new TabPane
29166  * @param {Object} config The config object
29167  */
29168
29169 Roo.bootstrap.dash.TabPane = function(config){
29170     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29171     
29172     this.addEvents({
29173         // raw events
29174         /**
29175          * @event activate
29176          * When a pane is activated
29177          * @param {Roo.bootstrap.dash.TabPane} pane
29178          */
29179         "activate" : true
29180          
29181     });
29182 };
29183
29184 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29185     
29186     active : false,
29187     title : '',
29188     
29189     // the tabBox that this is attached to.
29190     tab : false,
29191      
29192     getAutoCreate : function() 
29193     {
29194         var cfg = {
29195             tag: 'div',
29196             cls: 'tab-pane'
29197         };
29198         
29199         if(this.active){
29200             cfg.cls += ' active';
29201         }
29202         
29203         return cfg;
29204     },
29205     initEvents  : function()
29206     {
29207         //Roo.log('trigger add pane handler');
29208         this.parent().fireEvent('addpane', this)
29209     },
29210     
29211      /**
29212      * Updates the tab title 
29213      * @param {String} html to set the title to.
29214      */
29215     setTitle: function(str)
29216     {
29217         if (!this.tab) {
29218             return;
29219         }
29220         this.title = str;
29221         this.tab.select('a', true).first().dom.innerHTML = str;
29222         
29223     }
29224     
29225     
29226     
29227 });
29228
29229  
29230
29231
29232  /*
29233  * - LGPL
29234  *
29235  * Tooltip
29236  * 
29237  */
29238
29239 /**
29240  * @class Roo.bootstrap.Tooltip
29241  * Bootstrap Tooltip class
29242  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29243  * to determine which dom element triggers the tooltip.
29244  * 
29245  * It needs to add support for additional attributes like tooltip-position
29246  * 
29247  * @constructor
29248  * Create a new Toolti
29249  * @param {Object} config The config object
29250  */
29251
29252 Roo.bootstrap.Tooltip = function(config){
29253     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29254     
29255     this.alignment = Roo.bootstrap.Tooltip.alignment;
29256     
29257     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29258         this.alignment = config.alignment;
29259     }
29260     
29261 };
29262
29263 Roo.apply(Roo.bootstrap.Tooltip, {
29264     /**
29265      * @function init initialize tooltip monitoring.
29266      * @static
29267      */
29268     currentEl : false,
29269     currentTip : false,
29270     currentRegion : false,
29271     
29272     //  init : delay?
29273     
29274     init : function()
29275     {
29276         Roo.get(document).on('mouseover', this.enter ,this);
29277         Roo.get(document).on('mouseout', this.leave, this);
29278          
29279         
29280         this.currentTip = new Roo.bootstrap.Tooltip();
29281     },
29282     
29283     enter : function(ev)
29284     {
29285         var dom = ev.getTarget();
29286         
29287         //Roo.log(['enter',dom]);
29288         var el = Roo.fly(dom);
29289         if (this.currentEl) {
29290             //Roo.log(dom);
29291             //Roo.log(this.currentEl);
29292             //Roo.log(this.currentEl.contains(dom));
29293             if (this.currentEl == el) {
29294                 return;
29295             }
29296             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29297                 return;
29298             }
29299
29300         }
29301         
29302         if (this.currentTip.el) {
29303             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29304         }    
29305         //Roo.log(ev);
29306         
29307         if(!el || el.dom == document){
29308             return;
29309         }
29310         
29311         var bindEl = el; 
29312         var pel = false;
29313         if (!el.attr('tooltip')) {
29314             pel = el.findParent("[tooltip]");
29315             if (pel) {
29316                 bindEl = Roo.get(pel);
29317             }
29318         }
29319         
29320        
29321         
29322         // you can not look for children, as if el is the body.. then everythign is the child..
29323         if (!pel && !el.attr('tooltip')) { //
29324             if (!el.select("[tooltip]").elements.length) {
29325                 return;
29326             }
29327             // is the mouse over this child...?
29328             bindEl = el.select("[tooltip]").first();
29329             var xy = ev.getXY();
29330             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29331                 //Roo.log("not in region.");
29332                 return;
29333             }
29334             //Roo.log("child element over..");
29335             
29336         }
29337         this.currentEl = el;
29338         this.currentTip.bind(bindEl);
29339         this.currentRegion = Roo.lib.Region.getRegion(dom);
29340         this.currentTip.enter();
29341         
29342     },
29343     leave : function(ev)
29344     {
29345         var dom = ev.getTarget();
29346         //Roo.log(['leave',dom]);
29347         if (!this.currentEl) {
29348             return;
29349         }
29350         
29351         
29352         if (dom != this.currentEl.dom) {
29353             return;
29354         }
29355         var xy = ev.getXY();
29356         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29357             return;
29358         }
29359         // only activate leave if mouse cursor is outside... bounding box..
29360         
29361         
29362         
29363         
29364         if (this.currentTip) {
29365             this.currentTip.leave();
29366         }
29367         //Roo.log('clear currentEl');
29368         this.currentEl = false;
29369         
29370         
29371     },
29372     alignment : {
29373         'left' : ['r-l', [-2,0], 'right'],
29374         'right' : ['l-r', [2,0], 'left'],
29375         'bottom' : ['t-b', [0,2], 'top'],
29376         'top' : [ 'b-t', [0,-2], 'bottom']
29377     }
29378     
29379 });
29380
29381
29382 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29383     
29384     
29385     bindEl : false,
29386     
29387     delay : null, // can be { show : 300 , hide: 500}
29388     
29389     timeout : null,
29390     
29391     hoverState : null, //???
29392     
29393     placement : 'bottom', 
29394     
29395     alignment : false,
29396     
29397     getAutoCreate : function(){
29398     
29399         var cfg = {
29400            cls : 'tooltip',   
29401            role : 'tooltip',
29402            cn : [
29403                 {
29404                     cls : 'tooltip-arrow arrow'
29405                 },
29406                 {
29407                     cls : 'tooltip-inner'
29408                 }
29409            ]
29410         };
29411         
29412         return cfg;
29413     },
29414     bind : function(el)
29415     {
29416         this.bindEl = el;
29417     },
29418     
29419     initEvents : function()
29420     {
29421         this.arrowEl = this.el.select('.arrow', true).first();
29422         this.innerEl = this.el.select('.tooltip-inner', true).first();
29423     },
29424     
29425     enter : function () {
29426        
29427         if (this.timeout != null) {
29428             clearTimeout(this.timeout);
29429         }
29430         
29431         this.hoverState = 'in';
29432          //Roo.log("enter - show");
29433         if (!this.delay || !this.delay.show) {
29434             this.show();
29435             return;
29436         }
29437         var _t = this;
29438         this.timeout = setTimeout(function () {
29439             if (_t.hoverState == 'in') {
29440                 _t.show();
29441             }
29442         }, this.delay.show);
29443     },
29444     leave : function()
29445     {
29446         clearTimeout(this.timeout);
29447     
29448         this.hoverState = 'out';
29449          if (!this.delay || !this.delay.hide) {
29450             this.hide();
29451             return;
29452         }
29453        
29454         var _t = this;
29455         this.timeout = setTimeout(function () {
29456             //Roo.log("leave - timeout");
29457             
29458             if (_t.hoverState == 'out') {
29459                 _t.hide();
29460                 Roo.bootstrap.Tooltip.currentEl = false;
29461             }
29462         }, delay);
29463     },
29464     
29465     show : function (msg)
29466     {
29467         if (!this.el) {
29468             this.render(document.body);
29469         }
29470         // set content.
29471         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29472         
29473         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29474         
29475         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29476         
29477         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29478                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29479         
29480         var placement = typeof this.placement == 'function' ?
29481             this.placement.call(this, this.el, on_el) :
29482             this.placement;
29483             
29484         var autoToken = /\s?auto?\s?/i;
29485         var autoPlace = autoToken.test(placement);
29486         if (autoPlace) {
29487             placement = placement.replace(autoToken, '') || 'top';
29488         }
29489         
29490         //this.el.detach()
29491         //this.el.setXY([0,0]);
29492         this.el.show();
29493         //this.el.dom.style.display='block';
29494         
29495         //this.el.appendTo(on_el);
29496         
29497         var p = this.getPosition();
29498         var box = this.el.getBox();
29499         
29500         if (autoPlace) {
29501             // fixme..
29502         }
29503         
29504         var align = this.alignment[placement];
29505         
29506         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29507         
29508         if(placement == 'top' || placement == 'bottom'){
29509             if(xy[0] < 0){
29510                 placement = 'right';
29511             }
29512             
29513             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29514                 placement = 'left';
29515             }
29516             
29517             var scroll = Roo.select('body', true).first().getScroll();
29518             
29519             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29520                 placement = 'top';
29521             }
29522             
29523             align = this.alignment[placement];
29524             
29525             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29526             
29527         }
29528         
29529         var elems = document.getElementsByTagName('div');
29530         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29531         for (var i = 0; i < elems.length; i++) {
29532           var zindex = Number.parseInt(
29533                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29534                 10
29535           );
29536           if (zindex > highest) {
29537             highest = zindex;
29538           }
29539         }
29540         
29541         
29542         
29543         this.el.dom.style.zIndex = highest;
29544         
29545         this.el.alignTo(this.bindEl, align[0],align[1]);
29546         //var arrow = this.el.select('.arrow',true).first();
29547         //arrow.set(align[2], 
29548         
29549         this.el.addClass(placement);
29550         this.el.addClass("bs-tooltip-"+ placement);
29551         
29552         this.el.addClass('in fade show');
29553         
29554         this.hoverState = null;
29555         
29556         if (this.el.hasClass('fade')) {
29557             // fade it?
29558         }
29559         
29560         
29561         
29562         
29563         
29564     },
29565     hide : function()
29566     {
29567          
29568         if (!this.el) {
29569             return;
29570         }
29571         //this.el.setXY([0,0]);
29572         this.el.removeClass(['show', 'in']);
29573         //this.el.hide();
29574         
29575     }
29576     
29577 });
29578  
29579
29580  /*
29581  * - LGPL
29582  *
29583  * Location Picker
29584  * 
29585  */
29586
29587 /**
29588  * @class Roo.bootstrap.LocationPicker
29589  * @extends Roo.bootstrap.Component
29590  * Bootstrap LocationPicker class
29591  * @cfg {Number} latitude Position when init default 0
29592  * @cfg {Number} longitude Position when init default 0
29593  * @cfg {Number} zoom default 15
29594  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29595  * @cfg {Boolean} mapTypeControl default false
29596  * @cfg {Boolean} disableDoubleClickZoom default false
29597  * @cfg {Boolean} scrollwheel default true
29598  * @cfg {Boolean} streetViewControl default false
29599  * @cfg {Number} radius default 0
29600  * @cfg {String} locationName
29601  * @cfg {Boolean} draggable default true
29602  * @cfg {Boolean} enableAutocomplete default false
29603  * @cfg {Boolean} enableReverseGeocode default true
29604  * @cfg {String} markerTitle
29605  * 
29606  * @constructor
29607  * Create a new LocationPicker
29608  * @param {Object} config The config object
29609  */
29610
29611
29612 Roo.bootstrap.LocationPicker = function(config){
29613     
29614     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29615     
29616     this.addEvents({
29617         /**
29618          * @event initial
29619          * Fires when the picker initialized.
29620          * @param {Roo.bootstrap.LocationPicker} this
29621          * @param {Google Location} location
29622          */
29623         initial : true,
29624         /**
29625          * @event positionchanged
29626          * Fires when the picker position changed.
29627          * @param {Roo.bootstrap.LocationPicker} this
29628          * @param {Google Location} location
29629          */
29630         positionchanged : true,
29631         /**
29632          * @event resize
29633          * Fires when the map resize.
29634          * @param {Roo.bootstrap.LocationPicker} this
29635          */
29636         resize : true,
29637         /**
29638          * @event show
29639          * Fires when the map show.
29640          * @param {Roo.bootstrap.LocationPicker} this
29641          */
29642         show : true,
29643         /**
29644          * @event hide
29645          * Fires when the map hide.
29646          * @param {Roo.bootstrap.LocationPicker} this
29647          */
29648         hide : true,
29649         /**
29650          * @event mapClick
29651          * Fires when click the map.
29652          * @param {Roo.bootstrap.LocationPicker} this
29653          * @param {Map event} e
29654          */
29655         mapClick : true,
29656         /**
29657          * @event mapRightClick
29658          * Fires when right click the map.
29659          * @param {Roo.bootstrap.LocationPicker} this
29660          * @param {Map event} e
29661          */
29662         mapRightClick : true,
29663         /**
29664          * @event markerClick
29665          * Fires when click the marker.
29666          * @param {Roo.bootstrap.LocationPicker} this
29667          * @param {Map event} e
29668          */
29669         markerClick : true,
29670         /**
29671          * @event markerRightClick
29672          * Fires when right click the marker.
29673          * @param {Roo.bootstrap.LocationPicker} this
29674          * @param {Map event} e
29675          */
29676         markerRightClick : true,
29677         /**
29678          * @event OverlayViewDraw
29679          * Fires when OverlayView Draw
29680          * @param {Roo.bootstrap.LocationPicker} this
29681          */
29682         OverlayViewDraw : true,
29683         /**
29684          * @event OverlayViewOnAdd
29685          * Fires when OverlayView Draw
29686          * @param {Roo.bootstrap.LocationPicker} this
29687          */
29688         OverlayViewOnAdd : true,
29689         /**
29690          * @event OverlayViewOnRemove
29691          * Fires when OverlayView Draw
29692          * @param {Roo.bootstrap.LocationPicker} this
29693          */
29694         OverlayViewOnRemove : true,
29695         /**
29696          * @event OverlayViewShow
29697          * Fires when OverlayView Draw
29698          * @param {Roo.bootstrap.LocationPicker} this
29699          * @param {Pixel} cpx
29700          */
29701         OverlayViewShow : true,
29702         /**
29703          * @event OverlayViewHide
29704          * Fires when OverlayView Draw
29705          * @param {Roo.bootstrap.LocationPicker} this
29706          */
29707         OverlayViewHide : true,
29708         /**
29709          * @event loadexception
29710          * Fires when load google lib failed.
29711          * @param {Roo.bootstrap.LocationPicker} this
29712          */
29713         loadexception : true
29714     });
29715         
29716 };
29717
29718 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29719     
29720     gMapContext: false,
29721     
29722     latitude: 0,
29723     longitude: 0,
29724     zoom: 15,
29725     mapTypeId: false,
29726     mapTypeControl: false,
29727     disableDoubleClickZoom: false,
29728     scrollwheel: true,
29729     streetViewControl: false,
29730     radius: 0,
29731     locationName: '',
29732     draggable: true,
29733     enableAutocomplete: false,
29734     enableReverseGeocode: true,
29735     markerTitle: '',
29736     
29737     getAutoCreate: function()
29738     {
29739
29740         var cfg = {
29741             tag: 'div',
29742             cls: 'roo-location-picker'
29743         };
29744         
29745         return cfg
29746     },
29747     
29748     initEvents: function(ct, position)
29749     {       
29750         if(!this.el.getWidth() || this.isApplied()){
29751             return;
29752         }
29753         
29754         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29755         
29756         this.initial();
29757     },
29758     
29759     initial: function()
29760     {
29761         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29762             this.fireEvent('loadexception', this);
29763             return;
29764         }
29765         
29766         if(!this.mapTypeId){
29767             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29768         }
29769         
29770         this.gMapContext = this.GMapContext();
29771         
29772         this.initOverlayView();
29773         
29774         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29775         
29776         var _this = this;
29777                 
29778         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29779             _this.setPosition(_this.gMapContext.marker.position);
29780         });
29781         
29782         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29783             _this.fireEvent('mapClick', this, event);
29784             
29785         });
29786
29787         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29788             _this.fireEvent('mapRightClick', this, event);
29789             
29790         });
29791         
29792         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29793             _this.fireEvent('markerClick', this, event);
29794             
29795         });
29796
29797         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29798             _this.fireEvent('markerRightClick', this, event);
29799             
29800         });
29801         
29802         this.setPosition(this.gMapContext.location);
29803         
29804         this.fireEvent('initial', this, this.gMapContext.location);
29805     },
29806     
29807     initOverlayView: function()
29808     {
29809         var _this = this;
29810         
29811         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29812             
29813             draw: function()
29814             {
29815                 _this.fireEvent('OverlayViewDraw', _this);
29816             },
29817             
29818             onAdd: function()
29819             {
29820                 _this.fireEvent('OverlayViewOnAdd', _this);
29821             },
29822             
29823             onRemove: function()
29824             {
29825                 _this.fireEvent('OverlayViewOnRemove', _this);
29826             },
29827             
29828             show: function(cpx)
29829             {
29830                 _this.fireEvent('OverlayViewShow', _this, cpx);
29831             },
29832             
29833             hide: function()
29834             {
29835                 _this.fireEvent('OverlayViewHide', _this);
29836             }
29837             
29838         });
29839     },
29840     
29841     fromLatLngToContainerPixel: function(event)
29842     {
29843         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29844     },
29845     
29846     isApplied: function() 
29847     {
29848         return this.getGmapContext() == false ? false : true;
29849     },
29850     
29851     getGmapContext: function() 
29852     {
29853         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29854     },
29855     
29856     GMapContext: function() 
29857     {
29858         var position = new google.maps.LatLng(this.latitude, this.longitude);
29859         
29860         var _map = new google.maps.Map(this.el.dom, {
29861             center: position,
29862             zoom: this.zoom,
29863             mapTypeId: this.mapTypeId,
29864             mapTypeControl: this.mapTypeControl,
29865             disableDoubleClickZoom: this.disableDoubleClickZoom,
29866             scrollwheel: this.scrollwheel,
29867             streetViewControl: this.streetViewControl,
29868             locationName: this.locationName,
29869             draggable: this.draggable,
29870             enableAutocomplete: this.enableAutocomplete,
29871             enableReverseGeocode: this.enableReverseGeocode
29872         });
29873         
29874         var _marker = new google.maps.Marker({
29875             position: position,
29876             map: _map,
29877             title: this.markerTitle,
29878             draggable: this.draggable
29879         });
29880         
29881         return {
29882             map: _map,
29883             marker: _marker,
29884             circle: null,
29885             location: position,
29886             radius: this.radius,
29887             locationName: this.locationName,
29888             addressComponents: {
29889                 formatted_address: null,
29890                 addressLine1: null,
29891                 addressLine2: null,
29892                 streetName: null,
29893                 streetNumber: null,
29894                 city: null,
29895                 district: null,
29896                 state: null,
29897                 stateOrProvince: null
29898             },
29899             settings: this,
29900             domContainer: this.el.dom,
29901             geodecoder: new google.maps.Geocoder()
29902         };
29903     },
29904     
29905     drawCircle: function(center, radius, options) 
29906     {
29907         if (this.gMapContext.circle != null) {
29908             this.gMapContext.circle.setMap(null);
29909         }
29910         if (radius > 0) {
29911             radius *= 1;
29912             options = Roo.apply({}, options, {
29913                 strokeColor: "#0000FF",
29914                 strokeOpacity: .35,
29915                 strokeWeight: 2,
29916                 fillColor: "#0000FF",
29917                 fillOpacity: .2
29918             });
29919             
29920             options.map = this.gMapContext.map;
29921             options.radius = radius;
29922             options.center = center;
29923             this.gMapContext.circle = new google.maps.Circle(options);
29924             return this.gMapContext.circle;
29925         }
29926         
29927         return null;
29928     },
29929     
29930     setPosition: function(location) 
29931     {
29932         this.gMapContext.location = location;
29933         this.gMapContext.marker.setPosition(location);
29934         this.gMapContext.map.panTo(location);
29935         this.drawCircle(location, this.gMapContext.radius, {});
29936         
29937         var _this = this;
29938         
29939         if (this.gMapContext.settings.enableReverseGeocode) {
29940             this.gMapContext.geodecoder.geocode({
29941                 latLng: this.gMapContext.location
29942             }, function(results, status) {
29943                 
29944                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29945                     _this.gMapContext.locationName = results[0].formatted_address;
29946                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29947                     
29948                     _this.fireEvent('positionchanged', this, location);
29949                 }
29950             });
29951             
29952             return;
29953         }
29954         
29955         this.fireEvent('positionchanged', this, location);
29956     },
29957     
29958     resize: function()
29959     {
29960         google.maps.event.trigger(this.gMapContext.map, "resize");
29961         
29962         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29963         
29964         this.fireEvent('resize', this);
29965     },
29966     
29967     setPositionByLatLng: function(latitude, longitude)
29968     {
29969         this.setPosition(new google.maps.LatLng(latitude, longitude));
29970     },
29971     
29972     getCurrentPosition: function() 
29973     {
29974         return {
29975             latitude: this.gMapContext.location.lat(),
29976             longitude: this.gMapContext.location.lng()
29977         };
29978     },
29979     
29980     getAddressName: function() 
29981     {
29982         return this.gMapContext.locationName;
29983     },
29984     
29985     getAddressComponents: function() 
29986     {
29987         return this.gMapContext.addressComponents;
29988     },
29989     
29990     address_component_from_google_geocode: function(address_components) 
29991     {
29992         var result = {};
29993         
29994         for (var i = 0; i < address_components.length; i++) {
29995             var component = address_components[i];
29996             if (component.types.indexOf("postal_code") >= 0) {
29997                 result.postalCode = component.short_name;
29998             } else if (component.types.indexOf("street_number") >= 0) {
29999                 result.streetNumber = component.short_name;
30000             } else if (component.types.indexOf("route") >= 0) {
30001                 result.streetName = component.short_name;
30002             } else if (component.types.indexOf("neighborhood") >= 0) {
30003                 result.city = component.short_name;
30004             } else if (component.types.indexOf("locality") >= 0) {
30005                 result.city = component.short_name;
30006             } else if (component.types.indexOf("sublocality") >= 0) {
30007                 result.district = component.short_name;
30008             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30009                 result.stateOrProvince = component.short_name;
30010             } else if (component.types.indexOf("country") >= 0) {
30011                 result.country = component.short_name;
30012             }
30013         }
30014         
30015         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30016         result.addressLine2 = "";
30017         return result;
30018     },
30019     
30020     setZoomLevel: function(zoom)
30021     {
30022         this.gMapContext.map.setZoom(zoom);
30023     },
30024     
30025     show: function()
30026     {
30027         if(!this.el){
30028             return;
30029         }
30030         
30031         this.el.show();
30032         
30033         this.resize();
30034         
30035         this.fireEvent('show', this);
30036     },
30037     
30038     hide: function()
30039     {
30040         if(!this.el){
30041             return;
30042         }
30043         
30044         this.el.hide();
30045         
30046         this.fireEvent('hide', this);
30047     }
30048     
30049 });
30050
30051 Roo.apply(Roo.bootstrap.LocationPicker, {
30052     
30053     OverlayView : function(map, options)
30054     {
30055         options = options || {};
30056         
30057         this.setMap(map);
30058     }
30059     
30060     
30061 });/**
30062  * @class Roo.bootstrap.Alert
30063  * @extends Roo.bootstrap.Component
30064  * Bootstrap Alert class - shows an alert area box
30065  * eg
30066  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30067   Enter a valid email address
30068 </div>
30069  * @licence LGPL
30070  * @cfg {String} title The title of alert
30071  * @cfg {String} html The content of alert
30072  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30073  * @cfg {String} fa font-awesomeicon
30074  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30075  * @cfg {Boolean} close true to show a x closer
30076  * 
30077  * 
30078  * @constructor
30079  * Create a new alert
30080  * @param {Object} config The config object
30081  */
30082
30083
30084 Roo.bootstrap.Alert = function(config){
30085     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30086     
30087 };
30088
30089 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30090     
30091     title: '',
30092     html: '',
30093     weight: false,
30094     fa: false,
30095     faicon: false, // BC
30096     close : false,
30097     
30098     
30099     getAutoCreate : function()
30100     {
30101         
30102         var cfg = {
30103             tag : 'div',
30104             cls : 'alert',
30105             cn : [
30106                 {
30107                     tag: 'button',
30108                     type :  "button",
30109                     cls: "close",
30110                     html : '×',
30111                     style : this.close ? '' : 'display:none'
30112                 },
30113                 {
30114                     tag : 'i',
30115                     cls : 'roo-alert-icon'
30116                     
30117                 },
30118                 {
30119                     tag : 'b',
30120                     cls : 'roo-alert-title',
30121                     html : this.title
30122                 },
30123                 {
30124                     tag : 'span',
30125                     cls : 'roo-alert-text',
30126                     html : this.html
30127                 }
30128             ]
30129         };
30130         
30131         if(this.faicon){
30132             cfg.cn[0].cls += ' fa ' + this.faicon;
30133         }
30134         if(this.fa){
30135             cfg.cn[0].cls += ' fa ' + this.fa;
30136         }
30137         
30138         if(this.weight){
30139             cfg.cls += ' alert-' + this.weight;
30140         }
30141         
30142         return cfg;
30143     },
30144     
30145     initEvents: function() 
30146     {
30147         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30148         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30149         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30150         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30151         if (this.seconds > 0) {
30152             this.hide.defer(this.seconds, this);
30153         }
30154     },
30155     /**
30156      * Set the Title Message HTML
30157      * @param {String} html
30158      */
30159     setTitle : function(str)
30160     {
30161         this.titleEl.dom.innerHTML = str;
30162     },
30163      
30164      /**
30165      * Set the Body Message HTML
30166      * @param {String} html
30167      */
30168     setHtml : function(str)
30169     {
30170         this.htmlEl.dom.innerHTML = str;
30171     },
30172     /**
30173      * Set the Weight of the alert
30174      * @param {String} (success|info|warning|danger) weight
30175      */
30176     
30177     setWeight : function(weight)
30178     {
30179         if(this.weight){
30180             this.el.removeClass('alert-' + this.weight);
30181         }
30182         
30183         this.weight = weight;
30184         
30185         this.el.addClass('alert-' + this.weight);
30186     },
30187       /**
30188      * Set the Icon of the alert
30189      * @param {String} see fontawsome names (name without the 'fa-' bit)
30190      */
30191     setIcon : function(icon)
30192     {
30193         if(this.faicon){
30194             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30195         }
30196         
30197         this.faicon = icon;
30198         
30199         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30200     },
30201     /**
30202      * Hide the Alert
30203      */
30204     hide: function() 
30205     {
30206         this.el.hide();   
30207     },
30208     /**
30209      * Show the Alert
30210      */
30211     show: function() 
30212     {  
30213         this.el.show();   
30214     }
30215     
30216 });
30217
30218  
30219 /*
30220 * Licence: LGPL
30221 */
30222
30223 /**
30224  * @class Roo.bootstrap.UploadCropbox
30225  * @extends Roo.bootstrap.Component
30226  * Bootstrap UploadCropbox class
30227  * @cfg {String} emptyText show when image has been loaded
30228  * @cfg {String} rotateNotify show when image too small to rotate
30229  * @cfg {Number} errorTimeout default 3000
30230  * @cfg {Number} minWidth default 300
30231  * @cfg {Number} minHeight default 300
30232  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30233  * @cfg {Boolean} isDocument (true|false) default false
30234  * @cfg {String} url action url
30235  * @cfg {String} paramName default 'imageUpload'
30236  * @cfg {String} method default POST
30237  * @cfg {Boolean} loadMask (true|false) default true
30238  * @cfg {Boolean} loadingText default 'Loading...'
30239  * 
30240  * @constructor
30241  * Create a new UploadCropbox
30242  * @param {Object} config The config object
30243  */
30244
30245 Roo.bootstrap.UploadCropbox = function(config){
30246     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30247     
30248     this.addEvents({
30249         /**
30250          * @event beforeselectfile
30251          * Fire before select file
30252          * @param {Roo.bootstrap.UploadCropbox} this
30253          */
30254         "beforeselectfile" : true,
30255         /**
30256          * @event initial
30257          * Fire after initEvent
30258          * @param {Roo.bootstrap.UploadCropbox} this
30259          */
30260         "initial" : true,
30261         /**
30262          * @event crop
30263          * Fire after initEvent
30264          * @param {Roo.bootstrap.UploadCropbox} this
30265          * @param {String} data
30266          */
30267         "crop" : true,
30268         /**
30269          * @event prepare
30270          * Fire when preparing the file data
30271          * @param {Roo.bootstrap.UploadCropbox} this
30272          * @param {Object} file
30273          */
30274         "prepare" : true,
30275         /**
30276          * @event exception
30277          * Fire when get exception
30278          * @param {Roo.bootstrap.UploadCropbox} this
30279          * @param {XMLHttpRequest} xhr
30280          */
30281         "exception" : true,
30282         /**
30283          * @event beforeloadcanvas
30284          * Fire before load the canvas
30285          * @param {Roo.bootstrap.UploadCropbox} this
30286          * @param {String} src
30287          */
30288         "beforeloadcanvas" : true,
30289         /**
30290          * @event trash
30291          * Fire when trash image
30292          * @param {Roo.bootstrap.UploadCropbox} this
30293          */
30294         "trash" : true,
30295         /**
30296          * @event download
30297          * Fire when download the image
30298          * @param {Roo.bootstrap.UploadCropbox} this
30299          */
30300         "download" : true,
30301         /**
30302          * @event footerbuttonclick
30303          * Fire when footerbuttonclick
30304          * @param {Roo.bootstrap.UploadCropbox} this
30305          * @param {String} type
30306          */
30307         "footerbuttonclick" : true,
30308         /**
30309          * @event resize
30310          * Fire when resize
30311          * @param {Roo.bootstrap.UploadCropbox} this
30312          */
30313         "resize" : true,
30314         /**
30315          * @event rotate
30316          * Fire when rotate the image
30317          * @param {Roo.bootstrap.UploadCropbox} this
30318          * @param {String} pos
30319          */
30320         "rotate" : true,
30321         /**
30322          * @event inspect
30323          * Fire when inspect the file
30324          * @param {Roo.bootstrap.UploadCropbox} this
30325          * @param {Object} file
30326          */
30327         "inspect" : true,
30328         /**
30329          * @event upload
30330          * Fire when xhr upload the file
30331          * @param {Roo.bootstrap.UploadCropbox} this
30332          * @param {Object} data
30333          */
30334         "upload" : true,
30335         /**
30336          * @event arrange
30337          * Fire when arrange the file data
30338          * @param {Roo.bootstrap.UploadCropbox} this
30339          * @param {Object} formData
30340          */
30341         "arrange" : true
30342     });
30343     
30344     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30345 };
30346
30347 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30348     
30349     emptyText : 'Click to upload image',
30350     rotateNotify : 'Image is too small to rotate',
30351     errorTimeout : 3000,
30352     scale : 0,
30353     baseScale : 1,
30354     rotate : 0,
30355     dragable : false,
30356     pinching : false,
30357     mouseX : 0,
30358     mouseY : 0,
30359     cropData : false,
30360     minWidth : 300,
30361     minHeight : 300,
30362     file : false,
30363     exif : {},
30364     baseRotate : 1,
30365     cropType : 'image/jpeg',
30366     buttons : false,
30367     canvasLoaded : false,
30368     isDocument : false,
30369     method : 'POST',
30370     paramName : 'imageUpload',
30371     loadMask : true,
30372     loadingText : 'Loading...',
30373     maskEl : false,
30374     
30375     getAutoCreate : function()
30376     {
30377         var cfg = {
30378             tag : 'div',
30379             cls : 'roo-upload-cropbox',
30380             cn : [
30381                 {
30382                     tag : 'input',
30383                     cls : 'roo-upload-cropbox-selector',
30384                     type : 'file'
30385                 },
30386                 {
30387                     tag : 'div',
30388                     cls : 'roo-upload-cropbox-body',
30389                     style : 'cursor:pointer',
30390                     cn : [
30391                         {
30392                             tag : 'div',
30393                             cls : 'roo-upload-cropbox-preview'
30394                         },
30395                         {
30396                             tag : 'div',
30397                             cls : 'roo-upload-cropbox-thumb'
30398                         },
30399                         {
30400                             tag : 'div',
30401                             cls : 'roo-upload-cropbox-empty-notify',
30402                             html : this.emptyText
30403                         },
30404                         {
30405                             tag : 'div',
30406                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30407                             html : this.rotateNotify
30408                         }
30409                     ]
30410                 },
30411                 {
30412                     tag : 'div',
30413                     cls : 'roo-upload-cropbox-footer',
30414                     cn : {
30415                         tag : 'div',
30416                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30417                         cn : []
30418                     }
30419                 }
30420             ]
30421         };
30422         
30423         return cfg;
30424     },
30425     
30426     onRender : function(ct, position)
30427     {
30428         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30429         
30430         if (this.buttons.length) {
30431             
30432             Roo.each(this.buttons, function(bb) {
30433                 
30434                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30435                 
30436                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30437                 
30438             }, this);
30439         }
30440         
30441         if(this.loadMask){
30442             this.maskEl = this.el;
30443         }
30444     },
30445     
30446     initEvents : function()
30447     {
30448         this.urlAPI = (window.createObjectURL && window) || 
30449                                 (window.URL && URL.revokeObjectURL && URL) || 
30450                                 (window.webkitURL && webkitURL);
30451                         
30452         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30453         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30454         
30455         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30456         this.selectorEl.hide();
30457         
30458         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30459         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30460         
30461         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30462         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30463         this.thumbEl.hide();
30464         
30465         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30466         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30467         
30468         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30469         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30470         this.errorEl.hide();
30471         
30472         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30473         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30474         this.footerEl.hide();
30475         
30476         this.setThumbBoxSize();
30477         
30478         this.bind();
30479         
30480         this.resize();
30481         
30482         this.fireEvent('initial', this);
30483     },
30484
30485     bind : function()
30486     {
30487         var _this = this;
30488         
30489         window.addEventListener("resize", function() { _this.resize(); } );
30490         
30491         this.bodyEl.on('click', this.beforeSelectFile, this);
30492         
30493         if(Roo.isTouch){
30494             this.bodyEl.on('touchstart', this.onTouchStart, this);
30495             this.bodyEl.on('touchmove', this.onTouchMove, this);
30496             this.bodyEl.on('touchend', this.onTouchEnd, this);
30497         }
30498         
30499         if(!Roo.isTouch){
30500             this.bodyEl.on('mousedown', this.onMouseDown, this);
30501             this.bodyEl.on('mousemove', this.onMouseMove, this);
30502             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30503             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30504             Roo.get(document).on('mouseup', this.onMouseUp, this);
30505         }
30506         
30507         this.selectorEl.on('change', this.onFileSelected, this);
30508     },
30509     
30510     reset : function()
30511     {    
30512         this.scale = 0;
30513         this.baseScale = 1;
30514         this.rotate = 0;
30515         this.baseRotate = 1;
30516         this.dragable = false;
30517         this.pinching = false;
30518         this.mouseX = 0;
30519         this.mouseY = 0;
30520         this.cropData = false;
30521         this.notifyEl.dom.innerHTML = this.emptyText;
30522         
30523         this.selectorEl.dom.value = '';
30524         
30525     },
30526     
30527     resize : function()
30528     {
30529         if(this.fireEvent('resize', this) != false){
30530             this.setThumbBoxPosition();
30531             this.setCanvasPosition();
30532         }
30533     },
30534     
30535     onFooterButtonClick : function(e, el, o, type)
30536     {
30537         switch (type) {
30538             case 'rotate-left' :
30539                 this.onRotateLeft(e);
30540                 break;
30541             case 'rotate-right' :
30542                 this.onRotateRight(e);
30543                 break;
30544             case 'picture' :
30545                 this.beforeSelectFile(e);
30546                 break;
30547             case 'trash' :
30548                 this.trash(e);
30549                 break;
30550             case 'crop' :
30551                 this.crop(e);
30552                 break;
30553             case 'download' :
30554                 this.download(e);
30555                 break;
30556             default :
30557                 break;
30558         }
30559         
30560         this.fireEvent('footerbuttonclick', this, type);
30561     },
30562     
30563     beforeSelectFile : function(e)
30564     {
30565         e.preventDefault();
30566         
30567         if(this.fireEvent('beforeselectfile', this) != false){
30568             this.selectorEl.dom.click();
30569         }
30570     },
30571     
30572     onFileSelected : function(e)
30573     {
30574         e.preventDefault();
30575         
30576         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30577             return;
30578         }
30579         
30580         var file = this.selectorEl.dom.files[0];
30581         
30582         if(this.fireEvent('inspect', this, file) != false){
30583             this.prepare(file);
30584         }
30585         
30586     },
30587     
30588     trash : function(e)
30589     {
30590         this.fireEvent('trash', this);
30591     },
30592     
30593     download : function(e)
30594     {
30595         this.fireEvent('download', this);
30596     },
30597     
30598     loadCanvas : function(src)
30599     {   
30600         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30601             
30602             this.reset();
30603             
30604             this.imageEl = document.createElement('img');
30605             
30606             var _this = this;
30607             
30608             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30609             
30610             this.imageEl.src = src;
30611         }
30612     },
30613     
30614     onLoadCanvas : function()
30615     {   
30616         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30617         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30618         
30619         this.bodyEl.un('click', this.beforeSelectFile, this);
30620         
30621         this.notifyEl.hide();
30622         this.thumbEl.show();
30623         this.footerEl.show();
30624         
30625         this.baseRotateLevel();
30626         
30627         if(this.isDocument){
30628             this.setThumbBoxSize();
30629         }
30630         
30631         this.setThumbBoxPosition();
30632         
30633         this.baseScaleLevel();
30634         
30635         this.draw();
30636         
30637         this.resize();
30638         
30639         this.canvasLoaded = true;
30640         
30641         if(this.loadMask){
30642             this.maskEl.unmask();
30643         }
30644         
30645     },
30646     
30647     setCanvasPosition : function()
30648     {   
30649         if(!this.canvasEl){
30650             return;
30651         }
30652         
30653         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30654         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30655         
30656         this.previewEl.setLeft(pw);
30657         this.previewEl.setTop(ph);
30658         
30659     },
30660     
30661     onMouseDown : function(e)
30662     {   
30663         e.stopEvent();
30664         
30665         this.dragable = true;
30666         this.pinching = false;
30667         
30668         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30669             this.dragable = false;
30670             return;
30671         }
30672         
30673         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30674         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30675         
30676     },
30677     
30678     onMouseMove : function(e)
30679     {   
30680         e.stopEvent();
30681         
30682         if(!this.canvasLoaded){
30683             return;
30684         }
30685         
30686         if (!this.dragable){
30687             return;
30688         }
30689         
30690         var minX = Math.ceil(this.thumbEl.getLeft(true));
30691         var minY = Math.ceil(this.thumbEl.getTop(true));
30692         
30693         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30694         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30695         
30696         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30697         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30698         
30699         x = x - this.mouseX;
30700         y = y - this.mouseY;
30701         
30702         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30703         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30704         
30705         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30706         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30707         
30708         this.previewEl.setLeft(bgX);
30709         this.previewEl.setTop(bgY);
30710         
30711         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30712         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30713     },
30714     
30715     onMouseUp : function(e)
30716     {   
30717         e.stopEvent();
30718         
30719         this.dragable = false;
30720     },
30721     
30722     onMouseWheel : function(e)
30723     {   
30724         e.stopEvent();
30725         
30726         this.startScale = this.scale;
30727         
30728         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30729         
30730         if(!this.zoomable()){
30731             this.scale = this.startScale;
30732             return;
30733         }
30734         
30735         this.draw();
30736         
30737         return;
30738     },
30739     
30740     zoomable : function()
30741     {
30742         var minScale = this.thumbEl.getWidth() / this.minWidth;
30743         
30744         if(this.minWidth < this.minHeight){
30745             minScale = this.thumbEl.getHeight() / this.minHeight;
30746         }
30747         
30748         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30749         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30750         
30751         if(
30752                 this.isDocument &&
30753                 (this.rotate == 0 || this.rotate == 180) && 
30754                 (
30755                     width > this.imageEl.OriginWidth || 
30756                     height > this.imageEl.OriginHeight ||
30757                     (width < this.minWidth && height < this.minHeight)
30758                 )
30759         ){
30760             return false;
30761         }
30762         
30763         if(
30764                 this.isDocument &&
30765                 (this.rotate == 90 || this.rotate == 270) && 
30766                 (
30767                     width > this.imageEl.OriginWidth || 
30768                     height > this.imageEl.OriginHeight ||
30769                     (width < this.minHeight && height < this.minWidth)
30770                 )
30771         ){
30772             return false;
30773         }
30774         
30775         if(
30776                 !this.isDocument &&
30777                 (this.rotate == 0 || this.rotate == 180) && 
30778                 (
30779                     width < this.minWidth || 
30780                     width > this.imageEl.OriginWidth || 
30781                     height < this.minHeight || 
30782                     height > this.imageEl.OriginHeight
30783                 )
30784         ){
30785             return false;
30786         }
30787         
30788         if(
30789                 !this.isDocument &&
30790                 (this.rotate == 90 || this.rotate == 270) && 
30791                 (
30792                     width < this.minHeight || 
30793                     width > this.imageEl.OriginWidth || 
30794                     height < this.minWidth || 
30795                     height > this.imageEl.OriginHeight
30796                 )
30797         ){
30798             return false;
30799         }
30800         
30801         return true;
30802         
30803     },
30804     
30805     onRotateLeft : function(e)
30806     {   
30807         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30808             
30809             var minScale = this.thumbEl.getWidth() / this.minWidth;
30810             
30811             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30812             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30813             
30814             this.startScale = this.scale;
30815             
30816             while (this.getScaleLevel() < minScale){
30817             
30818                 this.scale = this.scale + 1;
30819                 
30820                 if(!this.zoomable()){
30821                     break;
30822                 }
30823                 
30824                 if(
30825                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30826                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30827                 ){
30828                     continue;
30829                 }
30830                 
30831                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30832
30833                 this.draw();
30834                 
30835                 return;
30836             }
30837             
30838             this.scale = this.startScale;
30839             
30840             this.onRotateFail();
30841             
30842             return false;
30843         }
30844         
30845         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30846
30847         if(this.isDocument){
30848             this.setThumbBoxSize();
30849             this.setThumbBoxPosition();
30850             this.setCanvasPosition();
30851         }
30852         
30853         this.draw();
30854         
30855         this.fireEvent('rotate', this, 'left');
30856         
30857     },
30858     
30859     onRotateRight : function(e)
30860     {
30861         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30862             
30863             var minScale = this.thumbEl.getWidth() / this.minWidth;
30864         
30865             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30866             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30867             
30868             this.startScale = this.scale;
30869             
30870             while (this.getScaleLevel() < minScale){
30871             
30872                 this.scale = this.scale + 1;
30873                 
30874                 if(!this.zoomable()){
30875                     break;
30876                 }
30877                 
30878                 if(
30879                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30880                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30881                 ){
30882                     continue;
30883                 }
30884                 
30885                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30886
30887                 this.draw();
30888                 
30889                 return;
30890             }
30891             
30892             this.scale = this.startScale;
30893             
30894             this.onRotateFail();
30895             
30896             return false;
30897         }
30898         
30899         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30900
30901         if(this.isDocument){
30902             this.setThumbBoxSize();
30903             this.setThumbBoxPosition();
30904             this.setCanvasPosition();
30905         }
30906         
30907         this.draw();
30908         
30909         this.fireEvent('rotate', this, 'right');
30910     },
30911     
30912     onRotateFail : function()
30913     {
30914         this.errorEl.show(true);
30915         
30916         var _this = this;
30917         
30918         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30919     },
30920     
30921     draw : function()
30922     {
30923         this.previewEl.dom.innerHTML = '';
30924         
30925         var canvasEl = document.createElement("canvas");
30926         
30927         var contextEl = canvasEl.getContext("2d");
30928         
30929         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30930         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30931         var center = this.imageEl.OriginWidth / 2;
30932         
30933         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30934             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30935             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30936             center = this.imageEl.OriginHeight / 2;
30937         }
30938         
30939         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30940         
30941         contextEl.translate(center, center);
30942         contextEl.rotate(this.rotate * Math.PI / 180);
30943
30944         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30945         
30946         this.canvasEl = document.createElement("canvas");
30947         
30948         this.contextEl = this.canvasEl.getContext("2d");
30949         
30950         switch (this.rotate) {
30951             case 0 :
30952                 
30953                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30954                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30955                 
30956                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30957                 
30958                 break;
30959             case 90 : 
30960                 
30961                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30962                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30963                 
30964                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30965                     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);
30966                     break;
30967                 }
30968                 
30969                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30970                 
30971                 break;
30972             case 180 :
30973                 
30974                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30975                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30976                 
30977                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30978                     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);
30979                     break;
30980                 }
30981                 
30982                 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);
30983                 
30984                 break;
30985             case 270 :
30986                 
30987                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30988                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30989         
30990                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30991                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30992                     break;
30993                 }
30994                 
30995                 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);
30996                 
30997                 break;
30998             default : 
30999                 break;
31000         }
31001         
31002         this.previewEl.appendChild(this.canvasEl);
31003         
31004         this.setCanvasPosition();
31005     },
31006     
31007     crop : function()
31008     {
31009         if(!this.canvasLoaded){
31010             return;
31011         }
31012         
31013         var imageCanvas = document.createElement("canvas");
31014         
31015         var imageContext = imageCanvas.getContext("2d");
31016         
31017         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31018         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31019         
31020         var center = imageCanvas.width / 2;
31021         
31022         imageContext.translate(center, center);
31023         
31024         imageContext.rotate(this.rotate * Math.PI / 180);
31025         
31026         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31027         
31028         var canvas = document.createElement("canvas");
31029         
31030         var context = canvas.getContext("2d");
31031                 
31032         canvas.width = this.minWidth;
31033         canvas.height = this.minHeight;
31034
31035         switch (this.rotate) {
31036             case 0 :
31037                 
31038                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31039                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31040                 
31041                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31042                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31043                 
31044                 var targetWidth = this.minWidth - 2 * x;
31045                 var targetHeight = this.minHeight - 2 * y;
31046                 
31047                 var scale = 1;
31048                 
31049                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31050                     scale = targetWidth / width;
31051                 }
31052                 
31053                 if(x > 0 && y == 0){
31054                     scale = targetHeight / height;
31055                 }
31056                 
31057                 if(x > 0 && y > 0){
31058                     scale = targetWidth / width;
31059                     
31060                     if(width < height){
31061                         scale = targetHeight / height;
31062                     }
31063                 }
31064                 
31065                 context.scale(scale, scale);
31066                 
31067                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31068                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31069
31070                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31071                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31072
31073                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31074                 
31075                 break;
31076             case 90 : 
31077                 
31078                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31079                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31080                 
31081                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31082                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31083                 
31084                 var targetWidth = this.minWidth - 2 * x;
31085                 var targetHeight = this.minHeight - 2 * y;
31086                 
31087                 var scale = 1;
31088                 
31089                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31090                     scale = targetWidth / width;
31091                 }
31092                 
31093                 if(x > 0 && y == 0){
31094                     scale = targetHeight / height;
31095                 }
31096                 
31097                 if(x > 0 && y > 0){
31098                     scale = targetWidth / width;
31099                     
31100                     if(width < height){
31101                         scale = targetHeight / height;
31102                     }
31103                 }
31104                 
31105                 context.scale(scale, scale);
31106                 
31107                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31108                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31109
31110                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31111                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31112                 
31113                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31114                 
31115                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31116                 
31117                 break;
31118             case 180 :
31119                 
31120                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31121                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31122                 
31123                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31124                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31125                 
31126                 var targetWidth = this.minWidth - 2 * x;
31127                 var targetHeight = this.minHeight - 2 * y;
31128                 
31129                 var scale = 1;
31130                 
31131                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31132                     scale = targetWidth / width;
31133                 }
31134                 
31135                 if(x > 0 && y == 0){
31136                     scale = targetHeight / height;
31137                 }
31138                 
31139                 if(x > 0 && y > 0){
31140                     scale = targetWidth / width;
31141                     
31142                     if(width < height){
31143                         scale = targetHeight / height;
31144                     }
31145                 }
31146                 
31147                 context.scale(scale, scale);
31148                 
31149                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31150                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31151
31152                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31153                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31154
31155                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31156                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31157                 
31158                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31159                 
31160                 break;
31161             case 270 :
31162                 
31163                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31164                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31165                 
31166                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31167                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31168                 
31169                 var targetWidth = this.minWidth - 2 * x;
31170                 var targetHeight = this.minHeight - 2 * y;
31171                 
31172                 var scale = 1;
31173                 
31174                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31175                     scale = targetWidth / width;
31176                 }
31177                 
31178                 if(x > 0 && y == 0){
31179                     scale = targetHeight / height;
31180                 }
31181                 
31182                 if(x > 0 && y > 0){
31183                     scale = targetWidth / width;
31184                     
31185                     if(width < height){
31186                         scale = targetHeight / height;
31187                     }
31188                 }
31189                 
31190                 context.scale(scale, scale);
31191                 
31192                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31193                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31194
31195                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31196                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31197                 
31198                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31199                 
31200                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31201                 
31202                 break;
31203             default : 
31204                 break;
31205         }
31206         
31207         this.cropData = canvas.toDataURL(this.cropType);
31208         
31209         if(this.fireEvent('crop', this, this.cropData) !== false){
31210             this.process(this.file, this.cropData);
31211         }
31212         
31213         return;
31214         
31215     },
31216     
31217     setThumbBoxSize : function()
31218     {
31219         var width, height;
31220         
31221         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31222             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31223             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31224             
31225             this.minWidth = width;
31226             this.minHeight = height;
31227             
31228             if(this.rotate == 90 || this.rotate == 270){
31229                 this.minWidth = height;
31230                 this.minHeight = width;
31231             }
31232         }
31233         
31234         height = 300;
31235         width = Math.ceil(this.minWidth * height / this.minHeight);
31236         
31237         if(this.minWidth > this.minHeight){
31238             width = 300;
31239             height = Math.ceil(this.minHeight * width / this.minWidth);
31240         }
31241         
31242         this.thumbEl.setStyle({
31243             width : width + 'px',
31244             height : height + 'px'
31245         });
31246
31247         return;
31248             
31249     },
31250     
31251     setThumbBoxPosition : function()
31252     {
31253         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31254         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31255         
31256         this.thumbEl.setLeft(x);
31257         this.thumbEl.setTop(y);
31258         
31259     },
31260     
31261     baseRotateLevel : function()
31262     {
31263         this.baseRotate = 1;
31264         
31265         if(
31266                 typeof(this.exif) != 'undefined' &&
31267                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31268                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31269         ){
31270             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31271         }
31272         
31273         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31274         
31275     },
31276     
31277     baseScaleLevel : function()
31278     {
31279         var width, height;
31280         
31281         if(this.isDocument){
31282             
31283             if(this.baseRotate == 6 || this.baseRotate == 8){
31284             
31285                 height = this.thumbEl.getHeight();
31286                 this.baseScale = height / this.imageEl.OriginWidth;
31287
31288                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31289                     width = this.thumbEl.getWidth();
31290                     this.baseScale = width / this.imageEl.OriginHeight;
31291                 }
31292
31293                 return;
31294             }
31295
31296             height = this.thumbEl.getHeight();
31297             this.baseScale = height / this.imageEl.OriginHeight;
31298
31299             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31300                 width = this.thumbEl.getWidth();
31301                 this.baseScale = width / this.imageEl.OriginWidth;
31302             }
31303
31304             return;
31305         }
31306         
31307         if(this.baseRotate == 6 || this.baseRotate == 8){
31308             
31309             width = this.thumbEl.getHeight();
31310             this.baseScale = width / this.imageEl.OriginHeight;
31311             
31312             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31313                 height = this.thumbEl.getWidth();
31314                 this.baseScale = height / this.imageEl.OriginHeight;
31315             }
31316             
31317             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31318                 height = this.thumbEl.getWidth();
31319                 this.baseScale = height / this.imageEl.OriginHeight;
31320                 
31321                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31322                     width = this.thumbEl.getHeight();
31323                     this.baseScale = width / this.imageEl.OriginWidth;
31324                 }
31325             }
31326             
31327             return;
31328         }
31329         
31330         width = this.thumbEl.getWidth();
31331         this.baseScale = width / this.imageEl.OriginWidth;
31332         
31333         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31334             height = this.thumbEl.getHeight();
31335             this.baseScale = height / this.imageEl.OriginHeight;
31336         }
31337         
31338         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31339             
31340             height = this.thumbEl.getHeight();
31341             this.baseScale = height / this.imageEl.OriginHeight;
31342             
31343             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31344                 width = this.thumbEl.getWidth();
31345                 this.baseScale = width / this.imageEl.OriginWidth;
31346             }
31347             
31348         }
31349         
31350         return;
31351     },
31352     
31353     getScaleLevel : function()
31354     {
31355         return this.baseScale * Math.pow(1.1, this.scale);
31356     },
31357     
31358     onTouchStart : function(e)
31359     {
31360         if(!this.canvasLoaded){
31361             this.beforeSelectFile(e);
31362             return;
31363         }
31364         
31365         var touches = e.browserEvent.touches;
31366         
31367         if(!touches){
31368             return;
31369         }
31370         
31371         if(touches.length == 1){
31372             this.onMouseDown(e);
31373             return;
31374         }
31375         
31376         if(touches.length != 2){
31377             return;
31378         }
31379         
31380         var coords = [];
31381         
31382         for(var i = 0, finger; finger = touches[i]; i++){
31383             coords.push(finger.pageX, finger.pageY);
31384         }
31385         
31386         var x = Math.pow(coords[0] - coords[2], 2);
31387         var y = Math.pow(coords[1] - coords[3], 2);
31388         
31389         this.startDistance = Math.sqrt(x + y);
31390         
31391         this.startScale = this.scale;
31392         
31393         this.pinching = true;
31394         this.dragable = false;
31395         
31396     },
31397     
31398     onTouchMove : function(e)
31399     {
31400         if(!this.pinching && !this.dragable){
31401             return;
31402         }
31403         
31404         var touches = e.browserEvent.touches;
31405         
31406         if(!touches){
31407             return;
31408         }
31409         
31410         if(this.dragable){
31411             this.onMouseMove(e);
31412             return;
31413         }
31414         
31415         var coords = [];
31416         
31417         for(var i = 0, finger; finger = touches[i]; i++){
31418             coords.push(finger.pageX, finger.pageY);
31419         }
31420         
31421         var x = Math.pow(coords[0] - coords[2], 2);
31422         var y = Math.pow(coords[1] - coords[3], 2);
31423         
31424         this.endDistance = Math.sqrt(x + y);
31425         
31426         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31427         
31428         if(!this.zoomable()){
31429             this.scale = this.startScale;
31430             return;
31431         }
31432         
31433         this.draw();
31434         
31435     },
31436     
31437     onTouchEnd : function(e)
31438     {
31439         this.pinching = false;
31440         this.dragable = false;
31441         
31442     },
31443     
31444     process : function(file, crop)
31445     {
31446         if(this.loadMask){
31447             this.maskEl.mask(this.loadingText);
31448         }
31449         
31450         this.xhr = new XMLHttpRequest();
31451         
31452         file.xhr = this.xhr;
31453
31454         this.xhr.open(this.method, this.url, true);
31455         
31456         var headers = {
31457             "Accept": "application/json",
31458             "Cache-Control": "no-cache",
31459             "X-Requested-With": "XMLHttpRequest"
31460         };
31461         
31462         for (var headerName in headers) {
31463             var headerValue = headers[headerName];
31464             if (headerValue) {
31465                 this.xhr.setRequestHeader(headerName, headerValue);
31466             }
31467         }
31468         
31469         var _this = this;
31470         
31471         this.xhr.onload = function()
31472         {
31473             _this.xhrOnLoad(_this.xhr);
31474         }
31475         
31476         this.xhr.onerror = function()
31477         {
31478             _this.xhrOnError(_this.xhr);
31479         }
31480         
31481         var formData = new FormData();
31482
31483         formData.append('returnHTML', 'NO');
31484         
31485         if(crop){
31486             formData.append('crop', crop);
31487         }
31488         
31489         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31490             formData.append(this.paramName, file, file.name);
31491         }
31492         
31493         if(typeof(file.filename) != 'undefined'){
31494             formData.append('filename', file.filename);
31495         }
31496         
31497         if(typeof(file.mimetype) != 'undefined'){
31498             formData.append('mimetype', file.mimetype);
31499         }
31500         
31501         if(this.fireEvent('arrange', this, formData) != false){
31502             this.xhr.send(formData);
31503         };
31504     },
31505     
31506     xhrOnLoad : function(xhr)
31507     {
31508         if(this.loadMask){
31509             this.maskEl.unmask();
31510         }
31511         
31512         if (xhr.readyState !== 4) {
31513             this.fireEvent('exception', this, xhr);
31514             return;
31515         }
31516
31517         var response = Roo.decode(xhr.responseText);
31518         
31519         if(!response.success){
31520             this.fireEvent('exception', this, xhr);
31521             return;
31522         }
31523         
31524         var response = Roo.decode(xhr.responseText);
31525         
31526         this.fireEvent('upload', this, response);
31527         
31528     },
31529     
31530     xhrOnError : function()
31531     {
31532         if(this.loadMask){
31533             this.maskEl.unmask();
31534         }
31535         
31536         Roo.log('xhr on error');
31537         
31538         var response = Roo.decode(xhr.responseText);
31539           
31540         Roo.log(response);
31541         
31542     },
31543     
31544     prepare : function(file)
31545     {   
31546         if(this.loadMask){
31547             this.maskEl.mask(this.loadingText);
31548         }
31549         
31550         this.file = false;
31551         this.exif = {};
31552         
31553         if(typeof(file) === 'string'){
31554             this.loadCanvas(file);
31555             return;
31556         }
31557         
31558         if(!file || !this.urlAPI){
31559             return;
31560         }
31561         
31562         this.file = file;
31563         this.cropType = file.type;
31564         
31565         var _this = this;
31566         
31567         if(this.fireEvent('prepare', this, this.file) != false){
31568             
31569             var reader = new FileReader();
31570             
31571             reader.onload = function (e) {
31572                 if (e.target.error) {
31573                     Roo.log(e.target.error);
31574                     return;
31575                 }
31576                 
31577                 var buffer = e.target.result,
31578                     dataView = new DataView(buffer),
31579                     offset = 2,
31580                     maxOffset = dataView.byteLength - 4,
31581                     markerBytes,
31582                     markerLength;
31583                 
31584                 if (dataView.getUint16(0) === 0xffd8) {
31585                     while (offset < maxOffset) {
31586                         markerBytes = dataView.getUint16(offset);
31587                         
31588                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31589                             markerLength = dataView.getUint16(offset + 2) + 2;
31590                             if (offset + markerLength > dataView.byteLength) {
31591                                 Roo.log('Invalid meta data: Invalid segment size.');
31592                                 break;
31593                             }
31594                             
31595                             if(markerBytes == 0xffe1){
31596                                 _this.parseExifData(
31597                                     dataView,
31598                                     offset,
31599                                     markerLength
31600                                 );
31601                             }
31602                             
31603                             offset += markerLength;
31604                             
31605                             continue;
31606                         }
31607                         
31608                         break;
31609                     }
31610                     
31611                 }
31612                 
31613                 var url = _this.urlAPI.createObjectURL(_this.file);
31614                 
31615                 _this.loadCanvas(url);
31616                 
31617                 return;
31618             }
31619             
31620             reader.readAsArrayBuffer(this.file);
31621             
31622         }
31623         
31624     },
31625     
31626     parseExifData : function(dataView, offset, length)
31627     {
31628         var tiffOffset = offset + 10,
31629             littleEndian,
31630             dirOffset;
31631     
31632         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31633             // No Exif data, might be XMP data instead
31634             return;
31635         }
31636         
31637         // Check for the ASCII code for "Exif" (0x45786966):
31638         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31639             // No Exif data, might be XMP data instead
31640             return;
31641         }
31642         if (tiffOffset + 8 > dataView.byteLength) {
31643             Roo.log('Invalid Exif data: Invalid segment size.');
31644             return;
31645         }
31646         // Check for the two null bytes:
31647         if (dataView.getUint16(offset + 8) !== 0x0000) {
31648             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31649             return;
31650         }
31651         // Check the byte alignment:
31652         switch (dataView.getUint16(tiffOffset)) {
31653         case 0x4949:
31654             littleEndian = true;
31655             break;
31656         case 0x4D4D:
31657             littleEndian = false;
31658             break;
31659         default:
31660             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31661             return;
31662         }
31663         // Check for the TIFF tag marker (0x002A):
31664         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31665             Roo.log('Invalid Exif data: Missing TIFF marker.');
31666             return;
31667         }
31668         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31669         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31670         
31671         this.parseExifTags(
31672             dataView,
31673             tiffOffset,
31674             tiffOffset + dirOffset,
31675             littleEndian
31676         );
31677     },
31678     
31679     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31680     {
31681         var tagsNumber,
31682             dirEndOffset,
31683             i;
31684         if (dirOffset + 6 > dataView.byteLength) {
31685             Roo.log('Invalid Exif data: Invalid directory offset.');
31686             return;
31687         }
31688         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31689         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31690         if (dirEndOffset + 4 > dataView.byteLength) {
31691             Roo.log('Invalid Exif data: Invalid directory size.');
31692             return;
31693         }
31694         for (i = 0; i < tagsNumber; i += 1) {
31695             this.parseExifTag(
31696                 dataView,
31697                 tiffOffset,
31698                 dirOffset + 2 + 12 * i, // tag offset
31699                 littleEndian
31700             );
31701         }
31702         // Return the offset to the next directory:
31703         return dataView.getUint32(dirEndOffset, littleEndian);
31704     },
31705     
31706     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31707     {
31708         var tag = dataView.getUint16(offset, littleEndian);
31709         
31710         this.exif[tag] = this.getExifValue(
31711             dataView,
31712             tiffOffset,
31713             offset,
31714             dataView.getUint16(offset + 2, littleEndian), // tag type
31715             dataView.getUint32(offset + 4, littleEndian), // tag length
31716             littleEndian
31717         );
31718     },
31719     
31720     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31721     {
31722         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31723             tagSize,
31724             dataOffset,
31725             values,
31726             i,
31727             str,
31728             c;
31729     
31730         if (!tagType) {
31731             Roo.log('Invalid Exif data: Invalid tag type.');
31732             return;
31733         }
31734         
31735         tagSize = tagType.size * length;
31736         // Determine if the value is contained in the dataOffset bytes,
31737         // or if the value at the dataOffset is a pointer to the actual data:
31738         dataOffset = tagSize > 4 ?
31739                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31740         if (dataOffset + tagSize > dataView.byteLength) {
31741             Roo.log('Invalid Exif data: Invalid data offset.');
31742             return;
31743         }
31744         if (length === 1) {
31745             return tagType.getValue(dataView, dataOffset, littleEndian);
31746         }
31747         values = [];
31748         for (i = 0; i < length; i += 1) {
31749             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31750         }
31751         
31752         if (tagType.ascii) {
31753             str = '';
31754             // Concatenate the chars:
31755             for (i = 0; i < values.length; i += 1) {
31756                 c = values[i];
31757                 // Ignore the terminating NULL byte(s):
31758                 if (c === '\u0000') {
31759                     break;
31760                 }
31761                 str += c;
31762             }
31763             return str;
31764         }
31765         return values;
31766     }
31767     
31768 });
31769
31770 Roo.apply(Roo.bootstrap.UploadCropbox, {
31771     tags : {
31772         'Orientation': 0x0112
31773     },
31774     
31775     Orientation: {
31776             1: 0, //'top-left',
31777 //            2: 'top-right',
31778             3: 180, //'bottom-right',
31779 //            4: 'bottom-left',
31780 //            5: 'left-top',
31781             6: 90, //'right-top',
31782 //            7: 'right-bottom',
31783             8: 270 //'left-bottom'
31784     },
31785     
31786     exifTagTypes : {
31787         // byte, 8-bit unsigned int:
31788         1: {
31789             getValue: function (dataView, dataOffset) {
31790                 return dataView.getUint8(dataOffset);
31791             },
31792             size: 1
31793         },
31794         // ascii, 8-bit byte:
31795         2: {
31796             getValue: function (dataView, dataOffset) {
31797                 return String.fromCharCode(dataView.getUint8(dataOffset));
31798             },
31799             size: 1,
31800             ascii: true
31801         },
31802         // short, 16 bit int:
31803         3: {
31804             getValue: function (dataView, dataOffset, littleEndian) {
31805                 return dataView.getUint16(dataOffset, littleEndian);
31806             },
31807             size: 2
31808         },
31809         // long, 32 bit int:
31810         4: {
31811             getValue: function (dataView, dataOffset, littleEndian) {
31812                 return dataView.getUint32(dataOffset, littleEndian);
31813             },
31814             size: 4
31815         },
31816         // rational = two long values, first is numerator, second is denominator:
31817         5: {
31818             getValue: function (dataView, dataOffset, littleEndian) {
31819                 return dataView.getUint32(dataOffset, littleEndian) /
31820                     dataView.getUint32(dataOffset + 4, littleEndian);
31821             },
31822             size: 8
31823         },
31824         // slong, 32 bit signed int:
31825         9: {
31826             getValue: function (dataView, dataOffset, littleEndian) {
31827                 return dataView.getInt32(dataOffset, littleEndian);
31828             },
31829             size: 4
31830         },
31831         // srational, two slongs, first is numerator, second is denominator:
31832         10: {
31833             getValue: function (dataView, dataOffset, littleEndian) {
31834                 return dataView.getInt32(dataOffset, littleEndian) /
31835                     dataView.getInt32(dataOffset + 4, littleEndian);
31836             },
31837             size: 8
31838         }
31839     },
31840     
31841     footer : {
31842         STANDARD : [
31843             {
31844                 tag : 'div',
31845                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31846                 action : 'rotate-left',
31847                 cn : [
31848                     {
31849                         tag : 'button',
31850                         cls : 'btn btn-default',
31851                         html : '<i class="fa fa-undo"></i>'
31852                     }
31853                 ]
31854             },
31855             {
31856                 tag : 'div',
31857                 cls : 'btn-group roo-upload-cropbox-picture',
31858                 action : 'picture',
31859                 cn : [
31860                     {
31861                         tag : 'button',
31862                         cls : 'btn btn-default',
31863                         html : '<i class="fa fa-picture-o"></i>'
31864                     }
31865                 ]
31866             },
31867             {
31868                 tag : 'div',
31869                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31870                 action : 'rotate-right',
31871                 cn : [
31872                     {
31873                         tag : 'button',
31874                         cls : 'btn btn-default',
31875                         html : '<i class="fa fa-repeat"></i>'
31876                     }
31877                 ]
31878             }
31879         ],
31880         DOCUMENT : [
31881             {
31882                 tag : 'div',
31883                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31884                 action : 'rotate-left',
31885                 cn : [
31886                     {
31887                         tag : 'button',
31888                         cls : 'btn btn-default',
31889                         html : '<i class="fa fa-undo"></i>'
31890                     }
31891                 ]
31892             },
31893             {
31894                 tag : 'div',
31895                 cls : 'btn-group roo-upload-cropbox-download',
31896                 action : 'download',
31897                 cn : [
31898                     {
31899                         tag : 'button',
31900                         cls : 'btn btn-default',
31901                         html : '<i class="fa fa-download"></i>'
31902                     }
31903                 ]
31904             },
31905             {
31906                 tag : 'div',
31907                 cls : 'btn-group roo-upload-cropbox-crop',
31908                 action : 'crop',
31909                 cn : [
31910                     {
31911                         tag : 'button',
31912                         cls : 'btn btn-default',
31913                         html : '<i class="fa fa-crop"></i>'
31914                     }
31915                 ]
31916             },
31917             {
31918                 tag : 'div',
31919                 cls : 'btn-group roo-upload-cropbox-trash',
31920                 action : 'trash',
31921                 cn : [
31922                     {
31923                         tag : 'button',
31924                         cls : 'btn btn-default',
31925                         html : '<i class="fa fa-trash"></i>'
31926                     }
31927                 ]
31928             },
31929             {
31930                 tag : 'div',
31931                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31932                 action : 'rotate-right',
31933                 cn : [
31934                     {
31935                         tag : 'button',
31936                         cls : 'btn btn-default',
31937                         html : '<i class="fa fa-repeat"></i>'
31938                     }
31939                 ]
31940             }
31941         ],
31942         ROTATOR : [
31943             {
31944                 tag : 'div',
31945                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31946                 action : 'rotate-left',
31947                 cn : [
31948                     {
31949                         tag : 'button',
31950                         cls : 'btn btn-default',
31951                         html : '<i class="fa fa-undo"></i>'
31952                     }
31953                 ]
31954             },
31955             {
31956                 tag : 'div',
31957                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31958                 action : 'rotate-right',
31959                 cn : [
31960                     {
31961                         tag : 'button',
31962                         cls : 'btn btn-default',
31963                         html : '<i class="fa fa-repeat"></i>'
31964                     }
31965                 ]
31966             }
31967         ]
31968     }
31969 });
31970
31971 /*
31972 * Licence: LGPL
31973 */
31974
31975 /**
31976  * @class Roo.bootstrap.DocumentManager
31977  * @extends Roo.bootstrap.Component
31978  * Bootstrap DocumentManager class
31979  * @cfg {String} paramName default 'imageUpload'
31980  * @cfg {String} toolTipName default 'filename'
31981  * @cfg {String} method default POST
31982  * @cfg {String} url action url
31983  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31984  * @cfg {Boolean} multiple multiple upload default true
31985  * @cfg {Number} thumbSize default 300
31986  * @cfg {String} fieldLabel
31987  * @cfg {Number} labelWidth default 4
31988  * @cfg {String} labelAlign (left|top) default left
31989  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31990 * @cfg {Number} labellg set the width of label (1-12)
31991  * @cfg {Number} labelmd set the width of label (1-12)
31992  * @cfg {Number} labelsm set the width of label (1-12)
31993  * @cfg {Number} labelxs set the width of label (1-12)
31994  * 
31995  * @constructor
31996  * Create a new DocumentManager
31997  * @param {Object} config The config object
31998  */
31999
32000 Roo.bootstrap.DocumentManager = function(config){
32001     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32002     
32003     this.files = [];
32004     this.delegates = [];
32005     
32006     this.addEvents({
32007         /**
32008          * @event initial
32009          * Fire when initial the DocumentManager
32010          * @param {Roo.bootstrap.DocumentManager} this
32011          */
32012         "initial" : true,
32013         /**
32014          * @event inspect
32015          * inspect selected file
32016          * @param {Roo.bootstrap.DocumentManager} this
32017          * @param {File} file
32018          */
32019         "inspect" : true,
32020         /**
32021          * @event exception
32022          * Fire when xhr load exception
32023          * @param {Roo.bootstrap.DocumentManager} this
32024          * @param {XMLHttpRequest} xhr
32025          */
32026         "exception" : true,
32027         /**
32028          * @event afterupload
32029          * Fire when xhr load exception
32030          * @param {Roo.bootstrap.DocumentManager} this
32031          * @param {XMLHttpRequest} xhr
32032          */
32033         "afterupload" : true,
32034         /**
32035          * @event prepare
32036          * prepare the form data
32037          * @param {Roo.bootstrap.DocumentManager} this
32038          * @param {Object} formData
32039          */
32040         "prepare" : true,
32041         /**
32042          * @event remove
32043          * Fire when remove the file
32044          * @param {Roo.bootstrap.DocumentManager} this
32045          * @param {Object} file
32046          */
32047         "remove" : true,
32048         /**
32049          * @event refresh
32050          * Fire after refresh the file
32051          * @param {Roo.bootstrap.DocumentManager} this
32052          */
32053         "refresh" : true,
32054         /**
32055          * @event click
32056          * Fire after click the image
32057          * @param {Roo.bootstrap.DocumentManager} this
32058          * @param {Object} file
32059          */
32060         "click" : true,
32061         /**
32062          * @event edit
32063          * Fire when upload a image and editable set to true
32064          * @param {Roo.bootstrap.DocumentManager} this
32065          * @param {Object} file
32066          */
32067         "edit" : true,
32068         /**
32069          * @event beforeselectfile
32070          * Fire before select file
32071          * @param {Roo.bootstrap.DocumentManager} this
32072          */
32073         "beforeselectfile" : true,
32074         /**
32075          * @event process
32076          * Fire before process file
32077          * @param {Roo.bootstrap.DocumentManager} this
32078          * @param {Object} file
32079          */
32080         "process" : true,
32081         /**
32082          * @event previewrendered
32083          * Fire when preview rendered
32084          * @param {Roo.bootstrap.DocumentManager} this
32085          * @param {Object} file
32086          */
32087         "previewrendered" : true,
32088         /**
32089          */
32090         "previewResize" : true
32091         
32092     });
32093 };
32094
32095 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32096     
32097     boxes : 0,
32098     inputName : '',
32099     thumbSize : 300,
32100     multiple : true,
32101     files : false,
32102     method : 'POST',
32103     url : '',
32104     paramName : 'imageUpload',
32105     toolTipName : 'filename',
32106     fieldLabel : '',
32107     labelWidth : 4,
32108     labelAlign : 'left',
32109     editable : true,
32110     delegates : false,
32111     xhr : false, 
32112     
32113     labellg : 0,
32114     labelmd : 0,
32115     labelsm : 0,
32116     labelxs : 0,
32117     
32118     getAutoCreate : function()
32119     {   
32120         var managerWidget = {
32121             tag : 'div',
32122             cls : 'roo-document-manager',
32123             cn : [
32124                 {
32125                     tag : 'input',
32126                     cls : 'roo-document-manager-selector',
32127                     type : 'file'
32128                 },
32129                 {
32130                     tag : 'div',
32131                     cls : 'roo-document-manager-uploader',
32132                     cn : [
32133                         {
32134                             tag : 'div',
32135                             cls : 'roo-document-manager-upload-btn',
32136                             html : '<i class="fa fa-plus"></i>'
32137                         }
32138                     ]
32139                     
32140                 }
32141             ]
32142         };
32143         
32144         var content = [
32145             {
32146                 tag : 'div',
32147                 cls : 'column col-md-12',
32148                 cn : managerWidget
32149             }
32150         ];
32151         
32152         if(this.fieldLabel.length){
32153             
32154             content = [
32155                 {
32156                     tag : 'div',
32157                     cls : 'column col-md-12',
32158                     html : this.fieldLabel
32159                 },
32160                 {
32161                     tag : 'div',
32162                     cls : 'column col-md-12',
32163                     cn : managerWidget
32164                 }
32165             ];
32166
32167             if(this.labelAlign == 'left'){
32168                 content = [
32169                     {
32170                         tag : 'div',
32171                         cls : 'column',
32172                         html : this.fieldLabel
32173                     },
32174                     {
32175                         tag : 'div',
32176                         cls : 'column',
32177                         cn : managerWidget
32178                     }
32179                 ];
32180                 
32181                 if(this.labelWidth > 12){
32182                     content[0].style = "width: " + this.labelWidth + 'px';
32183                 }
32184
32185                 if(this.labelWidth < 13 && this.labelmd == 0){
32186                     this.labelmd = this.labelWidth;
32187                 }
32188
32189                 if(this.labellg > 0){
32190                     content[0].cls += ' col-lg-' + this.labellg;
32191                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32192                 }
32193
32194                 if(this.labelmd > 0){
32195                     content[0].cls += ' col-md-' + this.labelmd;
32196                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32197                 }
32198
32199                 if(this.labelsm > 0){
32200                     content[0].cls += ' col-sm-' + this.labelsm;
32201                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32202                 }
32203
32204                 if(this.labelxs > 0){
32205                     content[0].cls += ' col-xs-' + this.labelxs;
32206                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32207                 }
32208                 
32209             }
32210         }
32211         
32212         var cfg = {
32213             tag : 'div',
32214             cls : 'row clearfix',
32215             cn : content
32216         };
32217         
32218         return cfg;
32219         
32220     },
32221     
32222     initEvents : function()
32223     {
32224         this.managerEl = this.el.select('.roo-document-manager', true).first();
32225         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32226         
32227         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32228         this.selectorEl.hide();
32229         
32230         if(this.multiple){
32231             this.selectorEl.attr('multiple', 'multiple');
32232         }
32233         
32234         this.selectorEl.on('change', this.onFileSelected, this);
32235         
32236         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32237         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32238         
32239         this.uploader.on('click', this.onUploaderClick, this);
32240         
32241         this.renderProgressDialog();
32242         
32243         var _this = this;
32244         
32245         window.addEventListener("resize", function() { _this.refresh(); } );
32246         
32247         this.fireEvent('initial', this);
32248     },
32249     
32250     renderProgressDialog : function()
32251     {
32252         var _this = this;
32253         
32254         this.progressDialog = new Roo.bootstrap.Modal({
32255             cls : 'roo-document-manager-progress-dialog',
32256             allow_close : false,
32257             animate : false,
32258             title : '',
32259             buttons : [
32260                 {
32261                     name  :'cancel',
32262                     weight : 'danger',
32263                     html : 'Cancel'
32264                 }
32265             ], 
32266             listeners : { 
32267                 btnclick : function() {
32268                     _this.uploadCancel();
32269                     this.hide();
32270                 }
32271             }
32272         });
32273          
32274         this.progressDialog.render(Roo.get(document.body));
32275          
32276         this.progress = new Roo.bootstrap.Progress({
32277             cls : 'roo-document-manager-progress',
32278             active : true,
32279             striped : true
32280         });
32281         
32282         this.progress.render(this.progressDialog.getChildContainer());
32283         
32284         this.progressBar = new Roo.bootstrap.ProgressBar({
32285             cls : 'roo-document-manager-progress-bar',
32286             aria_valuenow : 0,
32287             aria_valuemin : 0,
32288             aria_valuemax : 12,
32289             panel : 'success'
32290         });
32291         
32292         this.progressBar.render(this.progress.getChildContainer());
32293     },
32294     
32295     onUploaderClick : function(e)
32296     {
32297         e.preventDefault();
32298      
32299         if(this.fireEvent('beforeselectfile', this) != false){
32300             this.selectorEl.dom.click();
32301         }
32302         
32303     },
32304     
32305     onFileSelected : function(e)
32306     {
32307         e.preventDefault();
32308         
32309         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32310             return;
32311         }
32312         
32313         Roo.each(this.selectorEl.dom.files, function(file){
32314             if(this.fireEvent('inspect', this, file) != false){
32315                 this.files.push(file);
32316             }
32317         }, this);
32318         
32319         this.queue();
32320         
32321     },
32322     
32323     queue : function()
32324     {
32325         this.selectorEl.dom.value = '';
32326         
32327         if(!this.files || !this.files.length){
32328             return;
32329         }
32330         
32331         if(this.boxes > 0 && this.files.length > this.boxes){
32332             this.files = this.files.slice(0, this.boxes);
32333         }
32334         
32335         this.uploader.show();
32336         
32337         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32338             this.uploader.hide();
32339         }
32340         
32341         var _this = this;
32342         
32343         var files = [];
32344         
32345         var docs = [];
32346         
32347         Roo.each(this.files, function(file){
32348             
32349             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32350                 var f = this.renderPreview(file);
32351                 files.push(f);
32352                 return;
32353             }
32354             
32355             if(file.type.indexOf('image') != -1){
32356                 this.delegates.push(
32357                     (function(){
32358                         _this.process(file);
32359                     }).createDelegate(this)
32360                 );
32361         
32362                 return;
32363             }
32364             
32365             docs.push(
32366                 (function(){
32367                     _this.process(file);
32368                 }).createDelegate(this)
32369             );
32370             
32371         }, this);
32372         
32373         this.files = files;
32374         
32375         this.delegates = this.delegates.concat(docs);
32376         
32377         if(!this.delegates.length){
32378             this.refresh();
32379             return;
32380         }
32381         
32382         this.progressBar.aria_valuemax = this.delegates.length;
32383         
32384         this.arrange();
32385         
32386         return;
32387     },
32388     
32389     arrange : function()
32390     {
32391         if(!this.delegates.length){
32392             this.progressDialog.hide();
32393             this.refresh();
32394             return;
32395         }
32396         
32397         var delegate = this.delegates.shift();
32398         
32399         this.progressDialog.show();
32400         
32401         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32402         
32403         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32404         
32405         delegate();
32406     },
32407     
32408     refresh : function()
32409     {
32410         this.uploader.show();
32411         
32412         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32413             this.uploader.hide();
32414         }
32415         
32416         Roo.isTouch ? this.closable(false) : this.closable(true);
32417         
32418         this.fireEvent('refresh', this);
32419     },
32420     
32421     onRemove : function(e, el, o)
32422     {
32423         e.preventDefault();
32424         
32425         this.fireEvent('remove', this, o);
32426         
32427     },
32428     
32429     remove : function(o)
32430     {
32431         var files = [];
32432         
32433         Roo.each(this.files, function(file){
32434             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32435                 files.push(file);
32436                 return;
32437             }
32438
32439             o.target.remove();
32440
32441         }, this);
32442         
32443         this.files = files;
32444         
32445         this.refresh();
32446     },
32447     
32448     clear : function()
32449     {
32450         Roo.each(this.files, function(file){
32451             if(!file.target){
32452                 return;
32453             }
32454             
32455             file.target.remove();
32456
32457         }, this);
32458         
32459         this.files = [];
32460         
32461         this.refresh();
32462     },
32463     
32464     onClick : function(e, el, o)
32465     {
32466         e.preventDefault();
32467         
32468         this.fireEvent('click', this, o);
32469         
32470     },
32471     
32472     closable : function(closable)
32473     {
32474         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32475             
32476             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32477             
32478             if(closable){
32479                 el.show();
32480                 return;
32481             }
32482             
32483             el.hide();
32484             
32485         }, this);
32486     },
32487     
32488     xhrOnLoad : function(xhr)
32489     {
32490         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32491             el.remove();
32492         }, this);
32493         
32494         if (xhr.readyState !== 4) {
32495             this.arrange();
32496             this.fireEvent('exception', this, xhr);
32497             return;
32498         }
32499
32500         var response = Roo.decode(xhr.responseText);
32501         
32502         if(!response.success){
32503             this.arrange();
32504             this.fireEvent('exception', this, xhr);
32505             return;
32506         }
32507         
32508         var file = this.renderPreview(response.data);
32509         
32510         this.files.push(file);
32511         
32512         this.arrange();
32513         
32514         this.fireEvent('afterupload', this, xhr);
32515         
32516     },
32517     
32518     xhrOnError : function(xhr)
32519     {
32520         Roo.log('xhr on error');
32521         
32522         var response = Roo.decode(xhr.responseText);
32523           
32524         Roo.log(response);
32525         
32526         this.arrange();
32527     },
32528     
32529     process : function(file)
32530     {
32531         if(this.fireEvent('process', this, file) !== false){
32532             if(this.editable && file.type.indexOf('image') != -1){
32533                 this.fireEvent('edit', this, file);
32534                 return;
32535             }
32536
32537             this.uploadStart(file, false);
32538
32539             return;
32540         }
32541         
32542     },
32543     
32544     uploadStart : function(file, crop)
32545     {
32546         this.xhr = new XMLHttpRequest();
32547         
32548         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32549             this.arrange();
32550             return;
32551         }
32552         
32553         file.xhr = this.xhr;
32554             
32555         this.managerEl.createChild({
32556             tag : 'div',
32557             cls : 'roo-document-manager-loading',
32558             cn : [
32559                 {
32560                     tag : 'div',
32561                     tooltip : file.name,
32562                     cls : 'roo-document-manager-thumb',
32563                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32564                 }
32565             ]
32566
32567         });
32568
32569         this.xhr.open(this.method, this.url, true);
32570         
32571         var headers = {
32572             "Accept": "application/json",
32573             "Cache-Control": "no-cache",
32574             "X-Requested-With": "XMLHttpRequest"
32575         };
32576         
32577         for (var headerName in headers) {
32578             var headerValue = headers[headerName];
32579             if (headerValue) {
32580                 this.xhr.setRequestHeader(headerName, headerValue);
32581             }
32582         }
32583         
32584         var _this = this;
32585         
32586         this.xhr.onload = function()
32587         {
32588             _this.xhrOnLoad(_this.xhr);
32589         }
32590         
32591         this.xhr.onerror = function()
32592         {
32593             _this.xhrOnError(_this.xhr);
32594         }
32595         
32596         var formData = new FormData();
32597
32598         formData.append('returnHTML', 'NO');
32599         
32600         if(crop){
32601             formData.append('crop', crop);
32602         }
32603         
32604         formData.append(this.paramName, file, file.name);
32605         
32606         var options = {
32607             file : file, 
32608             manually : false
32609         };
32610         
32611         if(this.fireEvent('prepare', this, formData, options) != false){
32612             
32613             if(options.manually){
32614                 return;
32615             }
32616             
32617             this.xhr.send(formData);
32618             return;
32619         };
32620         
32621         this.uploadCancel();
32622     },
32623     
32624     uploadCancel : function()
32625     {
32626         if (this.xhr) {
32627             this.xhr.abort();
32628         }
32629         
32630         this.delegates = [];
32631         
32632         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32633             el.remove();
32634         }, this);
32635         
32636         this.arrange();
32637     },
32638     
32639     renderPreview : function(file)
32640     {
32641         if(typeof(file.target) != 'undefined' && file.target){
32642             return file;
32643         }
32644         
32645         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32646         
32647         var previewEl = this.managerEl.createChild({
32648             tag : 'div',
32649             cls : 'roo-document-manager-preview',
32650             cn : [
32651                 {
32652                     tag : 'div',
32653                     tooltip : file[this.toolTipName],
32654                     cls : 'roo-document-manager-thumb',
32655                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32656                 },
32657                 {
32658                     tag : 'button',
32659                     cls : 'close',
32660                     html : '<i class="fa fa-times-circle"></i>'
32661                 }
32662             ]
32663         });
32664
32665         var close = previewEl.select('button.close', true).first();
32666
32667         close.on('click', this.onRemove, this, file);
32668
32669         file.target = previewEl;
32670
32671         var image = previewEl.select('img', true).first();
32672         
32673         var _this = this;
32674         
32675         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32676         
32677         image.on('click', this.onClick, this, file);
32678         
32679         this.fireEvent('previewrendered', this, file);
32680         
32681         return file;
32682         
32683     },
32684     
32685     onPreviewLoad : function(file, image)
32686     {
32687         if(typeof(file.target) == 'undefined' || !file.target){
32688             return;
32689         }
32690         
32691         var width = image.dom.naturalWidth || image.dom.width;
32692         var height = image.dom.naturalHeight || image.dom.height;
32693         
32694         if(!this.previewResize) {
32695             return;
32696         }
32697         
32698         if(width > height){
32699             file.target.addClass('wide');
32700             return;
32701         }
32702         
32703         file.target.addClass('tall');
32704         return;
32705         
32706     },
32707     
32708     uploadFromSource : function(file, crop)
32709     {
32710         this.xhr = new XMLHttpRequest();
32711         
32712         this.managerEl.createChild({
32713             tag : 'div',
32714             cls : 'roo-document-manager-loading',
32715             cn : [
32716                 {
32717                     tag : 'div',
32718                     tooltip : file.name,
32719                     cls : 'roo-document-manager-thumb',
32720                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32721                 }
32722             ]
32723
32724         });
32725
32726         this.xhr.open(this.method, this.url, true);
32727         
32728         var headers = {
32729             "Accept": "application/json",
32730             "Cache-Control": "no-cache",
32731             "X-Requested-With": "XMLHttpRequest"
32732         };
32733         
32734         for (var headerName in headers) {
32735             var headerValue = headers[headerName];
32736             if (headerValue) {
32737                 this.xhr.setRequestHeader(headerName, headerValue);
32738             }
32739         }
32740         
32741         var _this = this;
32742         
32743         this.xhr.onload = function()
32744         {
32745             _this.xhrOnLoad(_this.xhr);
32746         }
32747         
32748         this.xhr.onerror = function()
32749         {
32750             _this.xhrOnError(_this.xhr);
32751         }
32752         
32753         var formData = new FormData();
32754
32755         formData.append('returnHTML', 'NO');
32756         
32757         formData.append('crop', crop);
32758         
32759         if(typeof(file.filename) != 'undefined'){
32760             formData.append('filename', file.filename);
32761         }
32762         
32763         if(typeof(file.mimetype) != 'undefined'){
32764             formData.append('mimetype', file.mimetype);
32765         }
32766         
32767         Roo.log(formData);
32768         
32769         if(this.fireEvent('prepare', this, formData) != false){
32770             this.xhr.send(formData);
32771         };
32772     }
32773 });
32774
32775 /*
32776 * Licence: LGPL
32777 */
32778
32779 /**
32780  * @class Roo.bootstrap.DocumentViewer
32781  * @extends Roo.bootstrap.Component
32782  * Bootstrap DocumentViewer class
32783  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32784  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32785  * 
32786  * @constructor
32787  * Create a new DocumentViewer
32788  * @param {Object} config The config object
32789  */
32790
32791 Roo.bootstrap.DocumentViewer = function(config){
32792     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32793     
32794     this.addEvents({
32795         /**
32796          * @event initial
32797          * Fire after initEvent
32798          * @param {Roo.bootstrap.DocumentViewer} this
32799          */
32800         "initial" : true,
32801         /**
32802          * @event click
32803          * Fire after click
32804          * @param {Roo.bootstrap.DocumentViewer} this
32805          */
32806         "click" : true,
32807         /**
32808          * @event download
32809          * Fire after download button
32810          * @param {Roo.bootstrap.DocumentViewer} this
32811          */
32812         "download" : true,
32813         /**
32814          * @event trash
32815          * Fire after trash button
32816          * @param {Roo.bootstrap.DocumentViewer} this
32817          */
32818         "trash" : true
32819         
32820     });
32821 };
32822
32823 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32824     
32825     showDownload : true,
32826     
32827     showTrash : true,
32828     
32829     getAutoCreate : function()
32830     {
32831         var cfg = {
32832             tag : 'div',
32833             cls : 'roo-document-viewer',
32834             cn : [
32835                 {
32836                     tag : 'div',
32837                     cls : 'roo-document-viewer-body',
32838                     cn : [
32839                         {
32840                             tag : 'div',
32841                             cls : 'roo-document-viewer-thumb',
32842                             cn : [
32843                                 {
32844                                     tag : 'img',
32845                                     cls : 'roo-document-viewer-image'
32846                                 }
32847                             ]
32848                         }
32849                     ]
32850                 },
32851                 {
32852                     tag : 'div',
32853                     cls : 'roo-document-viewer-footer',
32854                     cn : {
32855                         tag : 'div',
32856                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32857                         cn : [
32858                             {
32859                                 tag : 'div',
32860                                 cls : 'btn-group roo-document-viewer-download',
32861                                 cn : [
32862                                     {
32863                                         tag : 'button',
32864                                         cls : 'btn btn-default',
32865                                         html : '<i class="fa fa-download"></i>'
32866                                     }
32867                                 ]
32868                             },
32869                             {
32870                                 tag : 'div',
32871                                 cls : 'btn-group roo-document-viewer-trash',
32872                                 cn : [
32873                                     {
32874                                         tag : 'button',
32875                                         cls : 'btn btn-default',
32876                                         html : '<i class="fa fa-trash"></i>'
32877                                     }
32878                                 ]
32879                             }
32880                         ]
32881                     }
32882                 }
32883             ]
32884         };
32885         
32886         return cfg;
32887     },
32888     
32889     initEvents : function()
32890     {
32891         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32892         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32893         
32894         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32895         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32896         
32897         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32898         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32899         
32900         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32901         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32902         
32903         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32904         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32905         
32906         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32907         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32908         
32909         this.bodyEl.on('click', this.onClick, this);
32910         this.downloadBtn.on('click', this.onDownload, this);
32911         this.trashBtn.on('click', this.onTrash, this);
32912         
32913         this.downloadBtn.hide();
32914         this.trashBtn.hide();
32915         
32916         if(this.showDownload){
32917             this.downloadBtn.show();
32918         }
32919         
32920         if(this.showTrash){
32921             this.trashBtn.show();
32922         }
32923         
32924         if(!this.showDownload && !this.showTrash) {
32925             this.footerEl.hide();
32926         }
32927         
32928     },
32929     
32930     initial : function()
32931     {
32932         this.fireEvent('initial', this);
32933         
32934     },
32935     
32936     onClick : function(e)
32937     {
32938         e.preventDefault();
32939         
32940         this.fireEvent('click', this);
32941     },
32942     
32943     onDownload : function(e)
32944     {
32945         e.preventDefault();
32946         
32947         this.fireEvent('download', this);
32948     },
32949     
32950     onTrash : function(e)
32951     {
32952         e.preventDefault();
32953         
32954         this.fireEvent('trash', this);
32955     }
32956     
32957 });
32958 /*
32959  * - LGPL
32960  *
32961  * nav progress bar
32962  * 
32963  */
32964
32965 /**
32966  * @class Roo.bootstrap.NavProgressBar
32967  * @extends Roo.bootstrap.Component
32968  * Bootstrap NavProgressBar class
32969  * 
32970  * @constructor
32971  * Create a new nav progress bar - a bar indicating step along a process
32972  * @param {Object} config The config object
32973  */
32974
32975 Roo.bootstrap.NavProgressBar = function(config){
32976     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32977
32978     this.bullets = this.bullets || [];
32979    
32980 //    Roo.bootstrap.NavProgressBar.register(this);
32981      this.addEvents({
32982         /**
32983              * @event changed
32984              * Fires when the active item changes
32985              * @param {Roo.bootstrap.NavProgressBar} this
32986              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32987              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32988          */
32989         'changed': true
32990      });
32991     
32992 };
32993
32994 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32995     /**
32996      * @cfg {Roo.bootstrap.NavProgressItem} NavProgressBar:bullets[]
32997      * Bullets for the Nav Progress bar for the toolbar
32998      */
32999     bullets : [],
33000     barItems : [],
33001     
33002     getAutoCreate : function()
33003     {
33004         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33005         
33006         cfg = {
33007             tag : 'div',
33008             cls : 'roo-navigation-bar-group',
33009             cn : [
33010                 {
33011                     tag : 'div',
33012                     cls : 'roo-navigation-top-bar'
33013                 },
33014                 {
33015                     tag : 'div',
33016                     cls : 'roo-navigation-bullets-bar',
33017                     cn : [
33018                         {
33019                             tag : 'ul',
33020                             cls : 'roo-navigation-bar'
33021                         }
33022                     ]
33023                 },
33024                 
33025                 {
33026                     tag : 'div',
33027                     cls : 'roo-navigation-bottom-bar'
33028                 }
33029             ]
33030             
33031         };
33032         
33033         return cfg;
33034         
33035     },
33036     
33037     initEvents: function() 
33038     {
33039         
33040     },
33041     
33042     onRender : function(ct, position) 
33043     {
33044         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33045         
33046         if(this.bullets.length){
33047             Roo.each(this.bullets, function(b){
33048                this.addItem(b);
33049             }, this);
33050         }
33051         
33052         this.format();
33053         
33054     },
33055     
33056     addItem : function(cfg)
33057     {
33058         var item = new Roo.bootstrap.NavProgressItem(cfg);
33059         
33060         item.parentId = this.id;
33061         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33062         
33063         if(cfg.html){
33064             var top = new Roo.bootstrap.Element({
33065                 tag : 'div',
33066                 cls : 'roo-navigation-bar-text'
33067             });
33068             
33069             var bottom = new Roo.bootstrap.Element({
33070                 tag : 'div',
33071                 cls : 'roo-navigation-bar-text'
33072             });
33073             
33074             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33075             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33076             
33077             var topText = new Roo.bootstrap.Element({
33078                 tag : 'span',
33079                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33080             });
33081             
33082             var bottomText = new Roo.bootstrap.Element({
33083                 tag : 'span',
33084                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33085             });
33086             
33087             topText.onRender(top.el, null);
33088             bottomText.onRender(bottom.el, null);
33089             
33090             item.topEl = top;
33091             item.bottomEl = bottom;
33092         }
33093         
33094         this.barItems.push(item);
33095         
33096         return item;
33097     },
33098     
33099     getActive : function()
33100     {
33101         var active = false;
33102         
33103         Roo.each(this.barItems, function(v){
33104             
33105             if (!v.isActive()) {
33106                 return;
33107             }
33108             
33109             active = v;
33110             return false;
33111             
33112         });
33113         
33114         return active;
33115     },
33116     
33117     setActiveItem : function(item)
33118     {
33119         var prev = false;
33120         
33121         Roo.each(this.barItems, function(v){
33122             if (v.rid == item.rid) {
33123                 return ;
33124             }
33125             
33126             if (v.isActive()) {
33127                 v.setActive(false);
33128                 prev = v;
33129             }
33130         });
33131
33132         item.setActive(true);
33133         
33134         this.fireEvent('changed', this, item, prev);
33135     },
33136     
33137     getBarItem: function(rid)
33138     {
33139         var ret = false;
33140         
33141         Roo.each(this.barItems, function(e) {
33142             if (e.rid != rid) {
33143                 return;
33144             }
33145             
33146             ret =  e;
33147             return false;
33148         });
33149         
33150         return ret;
33151     },
33152     
33153     indexOfItem : function(item)
33154     {
33155         var index = false;
33156         
33157         Roo.each(this.barItems, function(v, i){
33158             
33159             if (v.rid != item.rid) {
33160                 return;
33161             }
33162             
33163             index = i;
33164             return false
33165         });
33166         
33167         return index;
33168     },
33169     
33170     setActiveNext : function()
33171     {
33172         var i = this.indexOfItem(this.getActive());
33173         
33174         if (i > this.barItems.length) {
33175             return;
33176         }
33177         
33178         this.setActiveItem(this.barItems[i+1]);
33179     },
33180     
33181     setActivePrev : function()
33182     {
33183         var i = this.indexOfItem(this.getActive());
33184         
33185         if (i  < 1) {
33186             return;
33187         }
33188         
33189         this.setActiveItem(this.barItems[i-1]);
33190     },
33191     
33192     format : function()
33193     {
33194         if(!this.barItems.length){
33195             return;
33196         }
33197      
33198         var width = 100 / this.barItems.length;
33199         
33200         Roo.each(this.barItems, function(i){
33201             i.el.setStyle('width', width + '%');
33202             i.topEl.el.setStyle('width', width + '%');
33203             i.bottomEl.el.setStyle('width', width + '%');
33204         }, this);
33205         
33206     }
33207     
33208 });
33209 /*
33210  * - LGPL
33211  *
33212  * Nav Progress Item
33213  * 
33214  */
33215
33216 /**
33217  * @class Roo.bootstrap.NavProgressItem
33218  * @extends Roo.bootstrap.Component
33219  * Bootstrap NavProgressItem class
33220  * @cfg {String} rid the reference id
33221  * @cfg {Boolean} active (true|false) Is item active default false
33222  * @cfg {Boolean} disabled (true|false) Is item active default false
33223  * @cfg {String} html
33224  * @cfg {String} position (top|bottom) text position default bottom
33225  * @cfg {String} icon show icon instead of number
33226  * 
33227  * @constructor
33228  * Create a new NavProgressItem
33229  * @param {Object} config The config object
33230  */
33231 Roo.bootstrap.NavProgressItem = function(config){
33232     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33233     this.addEvents({
33234         // raw events
33235         /**
33236          * @event click
33237          * The raw click event for the entire grid.
33238          * @param {Roo.bootstrap.NavProgressItem} this
33239          * @param {Roo.EventObject} e
33240          */
33241         "click" : true
33242     });
33243    
33244 };
33245
33246 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33247     
33248     rid : '',
33249     active : false,
33250     disabled : false,
33251     html : '',
33252     position : 'bottom',
33253     icon : false,
33254     
33255     getAutoCreate : function()
33256     {
33257         var iconCls = 'roo-navigation-bar-item-icon';
33258         
33259         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33260         
33261         var cfg = {
33262             tag: 'li',
33263             cls: 'roo-navigation-bar-item',
33264             cn : [
33265                 {
33266                     tag : 'i',
33267                     cls : iconCls
33268                 }
33269             ]
33270         };
33271         
33272         if(this.active){
33273             cfg.cls += ' active';
33274         }
33275         if(this.disabled){
33276             cfg.cls += ' disabled';
33277         }
33278         
33279         return cfg;
33280     },
33281     
33282     disable : function()
33283     {
33284         this.setDisabled(true);
33285     },
33286     
33287     enable : function()
33288     {
33289         this.setDisabled(false);
33290     },
33291     
33292     initEvents: function() 
33293     {
33294         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33295         
33296         this.iconEl.on('click', this.onClick, this);
33297     },
33298     
33299     onClick : function(e)
33300     {
33301         e.preventDefault();
33302         
33303         if(this.disabled){
33304             return;
33305         }
33306         
33307         if(this.fireEvent('click', this, e) === false){
33308             return;
33309         };
33310         
33311         this.parent().setActiveItem(this);
33312     },
33313     
33314     isActive: function () 
33315     {
33316         return this.active;
33317     },
33318     
33319     setActive : function(state)
33320     {
33321         if(this.active == state){
33322             return;
33323         }
33324         
33325         this.active = state;
33326         
33327         if (state) {
33328             this.el.addClass('active');
33329             return;
33330         }
33331         
33332         this.el.removeClass('active');
33333         
33334         return;
33335     },
33336     
33337     setDisabled : function(state)
33338     {
33339         if(this.disabled == state){
33340             return;
33341         }
33342         
33343         this.disabled = state;
33344         
33345         if (state) {
33346             this.el.addClass('disabled');
33347             return;
33348         }
33349         
33350         this.el.removeClass('disabled');
33351     },
33352     
33353     tooltipEl : function()
33354     {
33355         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33356     }
33357 });
33358  
33359
33360  /*
33361  * - LGPL
33362  *
33363  * FieldLabel
33364  * 
33365  */
33366
33367 /**
33368  * @class Roo.bootstrap.FieldLabel
33369  * @extends Roo.bootstrap.Component
33370  * Bootstrap FieldLabel class
33371  * @cfg {String} html contents of the element
33372  * @cfg {String} tag tag of the element default label
33373  * @cfg {String} cls class of the element
33374  * @cfg {String} target label target 
33375  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33376  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33377  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33378  * @cfg {String} iconTooltip default "This field is required"
33379  * @cfg {String} indicatorpos (left|right) default left
33380  * 
33381  * @constructor
33382  * Create a new FieldLabel
33383  * @param {Object} config The config object
33384  */
33385
33386 Roo.bootstrap.FieldLabel = function(config){
33387     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33388     
33389     this.addEvents({
33390             /**
33391              * @event invalid
33392              * Fires after the field has been marked as invalid.
33393              * @param {Roo.form.FieldLabel} this
33394              * @param {String} msg The validation message
33395              */
33396             invalid : true,
33397             /**
33398              * @event valid
33399              * Fires after the field has been validated with no errors.
33400              * @param {Roo.form.FieldLabel} this
33401              */
33402             valid : true
33403         });
33404 };
33405
33406 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33407     
33408     tag: 'label',
33409     cls: '',
33410     html: '',
33411     target: '',
33412     allowBlank : true,
33413     invalidClass : 'has-warning',
33414     validClass : 'has-success',
33415     iconTooltip : 'This field is required',
33416     indicatorpos : 'left',
33417     
33418     getAutoCreate : function(){
33419         
33420         var cls = "";
33421         if (!this.allowBlank) {
33422             cls  = "visible";
33423         }
33424         
33425         var cfg = {
33426             tag : this.tag,
33427             cls : 'roo-bootstrap-field-label ' + this.cls,
33428             for : this.target,
33429             cn : [
33430                 {
33431                     tag : 'i',
33432                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33433                     tooltip : this.iconTooltip
33434                 },
33435                 {
33436                     tag : 'span',
33437                     html : this.html
33438                 }
33439             ] 
33440         };
33441         
33442         if(this.indicatorpos == 'right'){
33443             var cfg = {
33444                 tag : this.tag,
33445                 cls : 'roo-bootstrap-field-label ' + this.cls,
33446                 for : this.target,
33447                 cn : [
33448                     {
33449                         tag : 'span',
33450                         html : this.html
33451                     },
33452                     {
33453                         tag : 'i',
33454                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33455                         tooltip : this.iconTooltip
33456                     }
33457                 ] 
33458             };
33459         }
33460         
33461         return cfg;
33462     },
33463     
33464     initEvents: function() 
33465     {
33466         Roo.bootstrap.Element.superclass.initEvents.call(this);
33467         
33468         this.indicator = this.indicatorEl();
33469         
33470         if(this.indicator){
33471             this.indicator.removeClass('visible');
33472             this.indicator.addClass('invisible');
33473         }
33474         
33475         Roo.bootstrap.FieldLabel.register(this);
33476     },
33477     
33478     indicatorEl : function()
33479     {
33480         var indicator = this.el.select('i.roo-required-indicator',true).first();
33481         
33482         if(!indicator){
33483             return false;
33484         }
33485         
33486         return indicator;
33487         
33488     },
33489     
33490     /**
33491      * Mark this field as valid
33492      */
33493     markValid : function()
33494     {
33495         if(this.indicator){
33496             this.indicator.removeClass('visible');
33497             this.indicator.addClass('invisible');
33498         }
33499         if (Roo.bootstrap.version == 3) {
33500             this.el.removeClass(this.invalidClass);
33501             this.el.addClass(this.validClass);
33502         } else {
33503             this.el.removeClass('is-invalid');
33504             this.el.addClass('is-valid');
33505         }
33506         
33507         
33508         this.fireEvent('valid', this);
33509     },
33510     
33511     /**
33512      * Mark this field as invalid
33513      * @param {String} msg The validation message
33514      */
33515     markInvalid : function(msg)
33516     {
33517         if(this.indicator){
33518             this.indicator.removeClass('invisible');
33519             this.indicator.addClass('visible');
33520         }
33521           if (Roo.bootstrap.version == 3) {
33522             this.el.removeClass(this.validClass);
33523             this.el.addClass(this.invalidClass);
33524         } else {
33525             this.el.removeClass('is-valid');
33526             this.el.addClass('is-invalid');
33527         }
33528         
33529         
33530         this.fireEvent('invalid', this, msg);
33531     }
33532     
33533    
33534 });
33535
33536 Roo.apply(Roo.bootstrap.FieldLabel, {
33537     
33538     groups: {},
33539     
33540      /**
33541     * register a FieldLabel Group
33542     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33543     */
33544     register : function(label)
33545     {
33546         if(this.groups.hasOwnProperty(label.target)){
33547             return;
33548         }
33549      
33550         this.groups[label.target] = label;
33551         
33552     },
33553     /**
33554     * fetch a FieldLabel Group based on the target
33555     * @param {string} target
33556     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33557     */
33558     get: function(target) {
33559         if (typeof(this.groups[target]) == 'undefined') {
33560             return false;
33561         }
33562         
33563         return this.groups[target] ;
33564     }
33565 });
33566
33567  
33568
33569  /*
33570  * - LGPL
33571  *
33572  * page DateSplitField.
33573  * 
33574  */
33575
33576
33577 /**
33578  * @class Roo.bootstrap.DateSplitField
33579  * @extends Roo.bootstrap.Component
33580  * Bootstrap DateSplitField class
33581  * @cfg {string} fieldLabel - the label associated
33582  * @cfg {Number} labelWidth set the width of label (0-12)
33583  * @cfg {String} labelAlign (top|left)
33584  * @cfg {Boolean} dayAllowBlank (true|false) default false
33585  * @cfg {Boolean} monthAllowBlank (true|false) default false
33586  * @cfg {Boolean} yearAllowBlank (true|false) default false
33587  * @cfg {string} dayPlaceholder 
33588  * @cfg {string} monthPlaceholder
33589  * @cfg {string} yearPlaceholder
33590  * @cfg {string} dayFormat default 'd'
33591  * @cfg {string} monthFormat default 'm'
33592  * @cfg {string} yearFormat default 'Y'
33593  * @cfg {Number} labellg set the width of label (1-12)
33594  * @cfg {Number} labelmd set the width of label (1-12)
33595  * @cfg {Number} labelsm set the width of label (1-12)
33596  * @cfg {Number} labelxs set the width of label (1-12)
33597
33598  *     
33599  * @constructor
33600  * Create a new DateSplitField
33601  * @param {Object} config The config object
33602  */
33603
33604 Roo.bootstrap.DateSplitField = function(config){
33605     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33606     
33607     this.addEvents({
33608         // raw events
33609          /**
33610          * @event years
33611          * getting the data of years
33612          * @param {Roo.bootstrap.DateSplitField} this
33613          * @param {Object} years
33614          */
33615         "years" : true,
33616         /**
33617          * @event days
33618          * getting the data of days
33619          * @param {Roo.bootstrap.DateSplitField} this
33620          * @param {Object} days
33621          */
33622         "days" : true,
33623         /**
33624          * @event invalid
33625          * Fires after the field has been marked as invalid.
33626          * @param {Roo.form.Field} this
33627          * @param {String} msg The validation message
33628          */
33629         invalid : true,
33630        /**
33631          * @event valid
33632          * Fires after the field has been validated with no errors.
33633          * @param {Roo.form.Field} this
33634          */
33635         valid : true
33636     });
33637 };
33638
33639 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33640     
33641     fieldLabel : '',
33642     labelAlign : 'top',
33643     labelWidth : 3,
33644     dayAllowBlank : false,
33645     monthAllowBlank : false,
33646     yearAllowBlank : false,
33647     dayPlaceholder : '',
33648     monthPlaceholder : '',
33649     yearPlaceholder : '',
33650     dayFormat : 'd',
33651     monthFormat : 'm',
33652     yearFormat : 'Y',
33653     isFormField : true,
33654     labellg : 0,
33655     labelmd : 0,
33656     labelsm : 0,
33657     labelxs : 0,
33658     
33659     getAutoCreate : function()
33660     {
33661         var cfg = {
33662             tag : 'div',
33663             cls : 'row roo-date-split-field-group',
33664             cn : [
33665                 {
33666                     tag : 'input',
33667                     type : 'hidden',
33668                     cls : 'form-hidden-field roo-date-split-field-group-value',
33669                     name : this.name
33670                 }
33671             ]
33672         };
33673         
33674         var labelCls = 'col-md-12';
33675         var contentCls = 'col-md-4';
33676         
33677         if(this.fieldLabel){
33678             
33679             var label = {
33680                 tag : 'div',
33681                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33682                 cn : [
33683                     {
33684                         tag : 'label',
33685                         html : this.fieldLabel
33686                     }
33687                 ]
33688             };
33689             
33690             if(this.labelAlign == 'left'){
33691             
33692                 if(this.labelWidth > 12){
33693                     label.style = "width: " + this.labelWidth + 'px';
33694                 }
33695
33696                 if(this.labelWidth < 13 && this.labelmd == 0){
33697                     this.labelmd = this.labelWidth;
33698                 }
33699
33700                 if(this.labellg > 0){
33701                     labelCls = ' col-lg-' + this.labellg;
33702                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33703                 }
33704
33705                 if(this.labelmd > 0){
33706                     labelCls = ' col-md-' + this.labelmd;
33707                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33708                 }
33709
33710                 if(this.labelsm > 0){
33711                     labelCls = ' col-sm-' + this.labelsm;
33712                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33713                 }
33714
33715                 if(this.labelxs > 0){
33716                     labelCls = ' col-xs-' + this.labelxs;
33717                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33718                 }
33719             }
33720             
33721             label.cls += ' ' + labelCls;
33722             
33723             cfg.cn.push(label);
33724         }
33725         
33726         Roo.each(['day', 'month', 'year'], function(t){
33727             cfg.cn.push({
33728                 tag : 'div',
33729                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33730             });
33731         }, this);
33732         
33733         return cfg;
33734     },
33735     
33736     inputEl: function ()
33737     {
33738         return this.el.select('.roo-date-split-field-group-value', true).first();
33739     },
33740     
33741     onRender : function(ct, position) 
33742     {
33743         var _this = this;
33744         
33745         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33746         
33747         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33748         
33749         this.dayField = new Roo.bootstrap.ComboBox({
33750             allowBlank : this.dayAllowBlank,
33751             alwaysQuery : true,
33752             displayField : 'value',
33753             editable : false,
33754             fieldLabel : '',
33755             forceSelection : true,
33756             mode : 'local',
33757             placeholder : this.dayPlaceholder,
33758             selectOnFocus : true,
33759             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33760             triggerAction : 'all',
33761             typeAhead : true,
33762             valueField : 'value',
33763             store : new Roo.data.SimpleStore({
33764                 data : (function() {    
33765                     var days = [];
33766                     _this.fireEvent('days', _this, days);
33767                     return days;
33768                 })(),
33769                 fields : [ 'value' ]
33770             }),
33771             listeners : {
33772                 select : function (_self, record, index)
33773                 {
33774                     _this.setValue(_this.getValue());
33775                 }
33776             }
33777         });
33778
33779         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33780         
33781         this.monthField = new Roo.bootstrap.MonthField({
33782             after : '<i class=\"fa fa-calendar\"></i>',
33783             allowBlank : this.monthAllowBlank,
33784             placeholder : this.monthPlaceholder,
33785             readOnly : true,
33786             listeners : {
33787                 render : function (_self)
33788                 {
33789                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33790                         e.preventDefault();
33791                         _self.focus();
33792                     });
33793                 },
33794                 select : function (_self, oldvalue, newvalue)
33795                 {
33796                     _this.setValue(_this.getValue());
33797                 }
33798             }
33799         });
33800         
33801         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33802         
33803         this.yearField = new Roo.bootstrap.ComboBox({
33804             allowBlank : this.yearAllowBlank,
33805             alwaysQuery : true,
33806             displayField : 'value',
33807             editable : false,
33808             fieldLabel : '',
33809             forceSelection : true,
33810             mode : 'local',
33811             placeholder : this.yearPlaceholder,
33812             selectOnFocus : true,
33813             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33814             triggerAction : 'all',
33815             typeAhead : true,
33816             valueField : 'value',
33817             store : new Roo.data.SimpleStore({
33818                 data : (function() {
33819                     var years = [];
33820                     _this.fireEvent('years', _this, years);
33821                     return years;
33822                 })(),
33823                 fields : [ 'value' ]
33824             }),
33825             listeners : {
33826                 select : function (_self, record, index)
33827                 {
33828                     _this.setValue(_this.getValue());
33829                 }
33830             }
33831         });
33832
33833         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33834     },
33835     
33836     setValue : function(v, format)
33837     {
33838         this.inputEl.dom.value = v;
33839         
33840         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33841         
33842         var d = Date.parseDate(v, f);
33843         
33844         if(!d){
33845             this.validate();
33846             return;
33847         }
33848         
33849         this.setDay(d.format(this.dayFormat));
33850         this.setMonth(d.format(this.monthFormat));
33851         this.setYear(d.format(this.yearFormat));
33852         
33853         this.validate();
33854         
33855         return;
33856     },
33857     
33858     setDay : function(v)
33859     {
33860         this.dayField.setValue(v);
33861         this.inputEl.dom.value = this.getValue();
33862         this.validate();
33863         return;
33864     },
33865     
33866     setMonth : function(v)
33867     {
33868         this.monthField.setValue(v, true);
33869         this.inputEl.dom.value = this.getValue();
33870         this.validate();
33871         return;
33872     },
33873     
33874     setYear : function(v)
33875     {
33876         this.yearField.setValue(v);
33877         this.inputEl.dom.value = this.getValue();
33878         this.validate();
33879         return;
33880     },
33881     
33882     getDay : function()
33883     {
33884         return this.dayField.getValue();
33885     },
33886     
33887     getMonth : function()
33888     {
33889         return this.monthField.getValue();
33890     },
33891     
33892     getYear : function()
33893     {
33894         return this.yearField.getValue();
33895     },
33896     
33897     getValue : function()
33898     {
33899         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33900         
33901         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33902         
33903         return date;
33904     },
33905     
33906     reset : function()
33907     {
33908         this.setDay('');
33909         this.setMonth('');
33910         this.setYear('');
33911         this.inputEl.dom.value = '';
33912         this.validate();
33913         return;
33914     },
33915     
33916     validate : function()
33917     {
33918         var d = this.dayField.validate();
33919         var m = this.monthField.validate();
33920         var y = this.yearField.validate();
33921         
33922         var valid = true;
33923         
33924         if(
33925                 (!this.dayAllowBlank && !d) ||
33926                 (!this.monthAllowBlank && !m) ||
33927                 (!this.yearAllowBlank && !y)
33928         ){
33929             valid = false;
33930         }
33931         
33932         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33933             return valid;
33934         }
33935         
33936         if(valid){
33937             this.markValid();
33938             return valid;
33939         }
33940         
33941         this.markInvalid();
33942         
33943         return valid;
33944     },
33945     
33946     markValid : function()
33947     {
33948         
33949         var label = this.el.select('label', true).first();
33950         var icon = this.el.select('i.fa-star', true).first();
33951
33952         if(label && icon){
33953             icon.remove();
33954         }
33955         
33956         this.fireEvent('valid', this);
33957     },
33958     
33959      /**
33960      * Mark this field as invalid
33961      * @param {String} msg The validation message
33962      */
33963     markInvalid : function(msg)
33964     {
33965         
33966         var label = this.el.select('label', true).first();
33967         var icon = this.el.select('i.fa-star', true).first();
33968
33969         if(label && !icon){
33970             this.el.select('.roo-date-split-field-label', true).createChild({
33971                 tag : 'i',
33972                 cls : 'text-danger fa fa-lg fa-star',
33973                 tooltip : 'This field is required',
33974                 style : 'margin-right:5px;'
33975             }, label, true);
33976         }
33977         
33978         this.fireEvent('invalid', this, msg);
33979     },
33980     
33981     clearInvalid : function()
33982     {
33983         var label = this.el.select('label', true).first();
33984         var icon = this.el.select('i.fa-star', true).first();
33985
33986         if(label && icon){
33987             icon.remove();
33988         }
33989         
33990         this.fireEvent('valid', this);
33991     },
33992     
33993     getName: function()
33994     {
33995         return this.name;
33996     }
33997     
33998 });
33999
34000  /**
34001  *
34002  * This is based on 
34003  * http://masonry.desandro.com
34004  *
34005  * The idea is to render all the bricks based on vertical width...
34006  *
34007  * The original code extends 'outlayer' - we might need to use that....
34008  * 
34009  */
34010
34011
34012 /**
34013  * @class Roo.bootstrap.LayoutMasonry
34014  * @extends Roo.bootstrap.Component
34015  * Bootstrap Layout Masonry class
34016  * 
34017  * @constructor
34018  * Create a new Element
34019  * @param {Object} config The config object
34020  */
34021
34022 Roo.bootstrap.LayoutMasonry = function(config){
34023     
34024     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34025     
34026     this.bricks = [];
34027     
34028     Roo.bootstrap.LayoutMasonry.register(this);
34029     
34030     this.addEvents({
34031         // raw events
34032         /**
34033          * @event layout
34034          * Fire after layout the items
34035          * @param {Roo.bootstrap.LayoutMasonry} this
34036          * @param {Roo.EventObject} e
34037          */
34038         "layout" : true
34039     });
34040     
34041 };
34042
34043 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34044     
34045     /**
34046      * @cfg {Boolean} isLayoutInstant = no animation?
34047      */   
34048     isLayoutInstant : false, // needed?
34049    
34050     /**
34051      * @cfg {Number} boxWidth  width of the columns
34052      */   
34053     boxWidth : 450,
34054     
34055       /**
34056      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34057      */   
34058     boxHeight : 0,
34059     
34060     /**
34061      * @cfg {Number} padWidth padding below box..
34062      */   
34063     padWidth : 10, 
34064     
34065     /**
34066      * @cfg {Number} gutter gutter width..
34067      */   
34068     gutter : 10,
34069     
34070      /**
34071      * @cfg {Number} maxCols maximum number of columns
34072      */   
34073     
34074     maxCols: 0,
34075     
34076     /**
34077      * @cfg {Boolean} isAutoInitial defalut true
34078      */   
34079     isAutoInitial : true, 
34080     
34081     containerWidth: 0,
34082     
34083     /**
34084      * @cfg {Boolean} isHorizontal defalut false
34085      */   
34086     isHorizontal : false, 
34087
34088     currentSize : null,
34089     
34090     tag: 'div',
34091     
34092     cls: '',
34093     
34094     bricks: null, //CompositeElement
34095     
34096     cols : 1,
34097     
34098     _isLayoutInited : false,
34099     
34100 //    isAlternative : false, // only use for vertical layout...
34101     
34102     /**
34103      * @cfg {Number} alternativePadWidth padding below box..
34104      */   
34105     alternativePadWidth : 50,
34106     
34107     selectedBrick : [],
34108     
34109     getAutoCreate : function(){
34110         
34111         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34112         
34113         var cfg = {
34114             tag: this.tag,
34115             cls: 'blog-masonary-wrapper ' + this.cls,
34116             cn : {
34117                 cls : 'mas-boxes masonary'
34118             }
34119         };
34120         
34121         return cfg;
34122     },
34123     
34124     getChildContainer: function( )
34125     {
34126         if (this.boxesEl) {
34127             return this.boxesEl;
34128         }
34129         
34130         this.boxesEl = this.el.select('.mas-boxes').first();
34131         
34132         return this.boxesEl;
34133     },
34134     
34135     
34136     initEvents : function()
34137     {
34138         var _this = this;
34139         
34140         if(this.isAutoInitial){
34141             Roo.log('hook children rendered');
34142             this.on('childrenrendered', function() {
34143                 Roo.log('children rendered');
34144                 _this.initial();
34145             } ,this);
34146         }
34147     },
34148     
34149     initial : function()
34150     {
34151         this.selectedBrick = [];
34152         
34153         this.currentSize = this.el.getBox(true);
34154         
34155         Roo.EventManager.onWindowResize(this.resize, this); 
34156
34157         if(!this.isAutoInitial){
34158             this.layout();
34159             return;
34160         }
34161         
34162         this.layout();
34163         
34164         return;
34165         //this.layout.defer(500,this);
34166         
34167     },
34168     
34169     resize : function()
34170     {
34171         var cs = this.el.getBox(true);
34172         
34173         if (
34174                 this.currentSize.width == cs.width && 
34175                 this.currentSize.x == cs.x && 
34176                 this.currentSize.height == cs.height && 
34177                 this.currentSize.y == cs.y 
34178         ) {
34179             Roo.log("no change in with or X or Y");
34180             return;
34181         }
34182         
34183         this.currentSize = cs;
34184         
34185         this.layout();
34186         
34187     },
34188     
34189     layout : function()
34190     {   
34191         this._resetLayout();
34192         
34193         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34194         
34195         this.layoutItems( isInstant );
34196       
34197         this._isLayoutInited = true;
34198         
34199         this.fireEvent('layout', this);
34200         
34201     },
34202     
34203     _resetLayout : function()
34204     {
34205         if(this.isHorizontal){
34206             this.horizontalMeasureColumns();
34207             return;
34208         }
34209         
34210         this.verticalMeasureColumns();
34211         
34212     },
34213     
34214     verticalMeasureColumns : function()
34215     {
34216         this.getContainerWidth();
34217         
34218 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34219 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34220 //            return;
34221 //        }
34222         
34223         var boxWidth = this.boxWidth + this.padWidth;
34224         
34225         if(this.containerWidth < this.boxWidth){
34226             boxWidth = this.containerWidth
34227         }
34228         
34229         var containerWidth = this.containerWidth;
34230         
34231         var cols = Math.floor(containerWidth / boxWidth);
34232         
34233         this.cols = Math.max( cols, 1 );
34234         
34235         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34236         
34237         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34238         
34239         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34240         
34241         this.colWidth = boxWidth + avail - this.padWidth;
34242         
34243         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34244         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34245     },
34246     
34247     horizontalMeasureColumns : function()
34248     {
34249         this.getContainerWidth();
34250         
34251         var boxWidth = this.boxWidth;
34252         
34253         if(this.containerWidth < boxWidth){
34254             boxWidth = this.containerWidth;
34255         }
34256         
34257         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34258         
34259         this.el.setHeight(boxWidth);
34260         
34261     },
34262     
34263     getContainerWidth : function()
34264     {
34265         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34266     },
34267     
34268     layoutItems : function( isInstant )
34269     {
34270         Roo.log(this.bricks);
34271         
34272         var items = Roo.apply([], this.bricks);
34273         
34274         if(this.isHorizontal){
34275             this._horizontalLayoutItems( items , isInstant );
34276             return;
34277         }
34278         
34279 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34280 //            this._verticalAlternativeLayoutItems( items , isInstant );
34281 //            return;
34282 //        }
34283         
34284         this._verticalLayoutItems( items , isInstant );
34285         
34286     },
34287     
34288     _verticalLayoutItems : function ( items , isInstant)
34289     {
34290         if ( !items || !items.length ) {
34291             return;
34292         }
34293         
34294         var standard = [
34295             ['xs', 'xs', 'xs', 'tall'],
34296             ['xs', 'xs', 'tall'],
34297             ['xs', 'xs', 'sm'],
34298             ['xs', 'xs', 'xs'],
34299             ['xs', 'tall'],
34300             ['xs', 'sm'],
34301             ['xs', 'xs'],
34302             ['xs'],
34303             
34304             ['sm', 'xs', 'xs'],
34305             ['sm', 'xs'],
34306             ['sm'],
34307             
34308             ['tall', 'xs', 'xs', 'xs'],
34309             ['tall', 'xs', 'xs'],
34310             ['tall', 'xs'],
34311             ['tall']
34312             
34313         ];
34314         
34315         var queue = [];
34316         
34317         var boxes = [];
34318         
34319         var box = [];
34320         
34321         Roo.each(items, function(item, k){
34322             
34323             switch (item.size) {
34324                 // these layouts take up a full box,
34325                 case 'md' :
34326                 case 'md-left' :
34327                 case 'md-right' :
34328                 case 'wide' :
34329                     
34330                     if(box.length){
34331                         boxes.push(box);
34332                         box = [];
34333                     }
34334                     
34335                     boxes.push([item]);
34336                     
34337                     break;
34338                     
34339                 case 'xs' :
34340                 case 'sm' :
34341                 case 'tall' :
34342                     
34343                     box.push(item);
34344                     
34345                     break;
34346                 default :
34347                     break;
34348                     
34349             }
34350             
34351         }, this);
34352         
34353         if(box.length){
34354             boxes.push(box);
34355             box = [];
34356         }
34357         
34358         var filterPattern = function(box, length)
34359         {
34360             if(!box.length){
34361                 return;
34362             }
34363             
34364             var match = false;
34365             
34366             var pattern = box.slice(0, length);
34367             
34368             var format = [];
34369             
34370             Roo.each(pattern, function(i){
34371                 format.push(i.size);
34372             }, this);
34373             
34374             Roo.each(standard, function(s){
34375                 
34376                 if(String(s) != String(format)){
34377                     return;
34378                 }
34379                 
34380                 match = true;
34381                 return false;
34382                 
34383             }, this);
34384             
34385             if(!match && length == 1){
34386                 return;
34387             }
34388             
34389             if(!match){
34390                 filterPattern(box, length - 1);
34391                 return;
34392             }
34393                 
34394             queue.push(pattern);
34395
34396             box = box.slice(length, box.length);
34397
34398             filterPattern(box, 4);
34399
34400             return;
34401             
34402         }
34403         
34404         Roo.each(boxes, function(box, k){
34405             
34406             if(!box.length){
34407                 return;
34408             }
34409             
34410             if(box.length == 1){
34411                 queue.push(box);
34412                 return;
34413             }
34414             
34415             filterPattern(box, 4);
34416             
34417         }, this);
34418         
34419         this._processVerticalLayoutQueue( queue, isInstant );
34420         
34421     },
34422     
34423 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34424 //    {
34425 //        if ( !items || !items.length ) {
34426 //            return;
34427 //        }
34428 //
34429 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34430 //        
34431 //    },
34432     
34433     _horizontalLayoutItems : function ( items , isInstant)
34434     {
34435         if ( !items || !items.length || items.length < 3) {
34436             return;
34437         }
34438         
34439         items.reverse();
34440         
34441         var eItems = items.slice(0, 3);
34442         
34443         items = items.slice(3, items.length);
34444         
34445         var standard = [
34446             ['xs', 'xs', 'xs', 'wide'],
34447             ['xs', 'xs', 'wide'],
34448             ['xs', 'xs', 'sm'],
34449             ['xs', 'xs', 'xs'],
34450             ['xs', 'wide'],
34451             ['xs', 'sm'],
34452             ['xs', 'xs'],
34453             ['xs'],
34454             
34455             ['sm', 'xs', 'xs'],
34456             ['sm', 'xs'],
34457             ['sm'],
34458             
34459             ['wide', 'xs', 'xs', 'xs'],
34460             ['wide', 'xs', 'xs'],
34461             ['wide', 'xs'],
34462             ['wide'],
34463             
34464             ['wide-thin']
34465         ];
34466         
34467         var queue = [];
34468         
34469         var boxes = [];
34470         
34471         var box = [];
34472         
34473         Roo.each(items, function(item, k){
34474             
34475             switch (item.size) {
34476                 case 'md' :
34477                 case 'md-left' :
34478                 case 'md-right' :
34479                 case 'tall' :
34480                     
34481                     if(box.length){
34482                         boxes.push(box);
34483                         box = [];
34484                     }
34485                     
34486                     boxes.push([item]);
34487                     
34488                     break;
34489                     
34490                 case 'xs' :
34491                 case 'sm' :
34492                 case 'wide' :
34493                 case 'wide-thin' :
34494                     
34495                     box.push(item);
34496                     
34497                     break;
34498                 default :
34499                     break;
34500                     
34501             }
34502             
34503         }, this);
34504         
34505         if(box.length){
34506             boxes.push(box);
34507             box = [];
34508         }
34509         
34510         var filterPattern = function(box, length)
34511         {
34512             if(!box.length){
34513                 return;
34514             }
34515             
34516             var match = false;
34517             
34518             var pattern = box.slice(0, length);
34519             
34520             var format = [];
34521             
34522             Roo.each(pattern, function(i){
34523                 format.push(i.size);
34524             }, this);
34525             
34526             Roo.each(standard, function(s){
34527                 
34528                 if(String(s) != String(format)){
34529                     return;
34530                 }
34531                 
34532                 match = true;
34533                 return false;
34534                 
34535             }, this);
34536             
34537             if(!match && length == 1){
34538                 return;
34539             }
34540             
34541             if(!match){
34542                 filterPattern(box, length - 1);
34543                 return;
34544             }
34545                 
34546             queue.push(pattern);
34547
34548             box = box.slice(length, box.length);
34549
34550             filterPattern(box, 4);
34551
34552             return;
34553             
34554         }
34555         
34556         Roo.each(boxes, function(box, k){
34557             
34558             if(!box.length){
34559                 return;
34560             }
34561             
34562             if(box.length == 1){
34563                 queue.push(box);
34564                 return;
34565             }
34566             
34567             filterPattern(box, 4);
34568             
34569         }, this);
34570         
34571         
34572         var prune = [];
34573         
34574         var pos = this.el.getBox(true);
34575         
34576         var minX = pos.x;
34577         
34578         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34579         
34580         var hit_end = false;
34581         
34582         Roo.each(queue, function(box){
34583             
34584             if(hit_end){
34585                 
34586                 Roo.each(box, function(b){
34587                 
34588                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34589                     b.el.hide();
34590
34591                 }, this);
34592
34593                 return;
34594             }
34595             
34596             var mx = 0;
34597             
34598             Roo.each(box, function(b){
34599                 
34600                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34601                 b.el.show();
34602
34603                 mx = Math.max(mx, b.x);
34604                 
34605             }, this);
34606             
34607             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34608             
34609             if(maxX < minX){
34610                 
34611                 Roo.each(box, function(b){
34612                 
34613                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34614                     b.el.hide();
34615                     
34616                 }, this);
34617                 
34618                 hit_end = true;
34619                 
34620                 return;
34621             }
34622             
34623             prune.push(box);
34624             
34625         }, this);
34626         
34627         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34628     },
34629     
34630     /** Sets position of item in DOM
34631     * @param {Element} item
34632     * @param {Number} x - horizontal position
34633     * @param {Number} y - vertical position
34634     * @param {Boolean} isInstant - disables transitions
34635     */
34636     _processVerticalLayoutQueue : function( queue, isInstant )
34637     {
34638         var pos = this.el.getBox(true);
34639         var x = pos.x;
34640         var y = pos.y;
34641         var maxY = [];
34642         
34643         for (var i = 0; i < this.cols; i++){
34644             maxY[i] = pos.y;
34645         }
34646         
34647         Roo.each(queue, function(box, k){
34648             
34649             var col = k % this.cols;
34650             
34651             Roo.each(box, function(b,kk){
34652                 
34653                 b.el.position('absolute');
34654                 
34655                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34656                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34657                 
34658                 if(b.size == 'md-left' || b.size == 'md-right'){
34659                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34660                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34661                 }
34662                 
34663                 b.el.setWidth(width);
34664                 b.el.setHeight(height);
34665                 // iframe?
34666                 b.el.select('iframe',true).setSize(width,height);
34667                 
34668             }, this);
34669             
34670             for (var i = 0; i < this.cols; i++){
34671                 
34672                 if(maxY[i] < maxY[col]){
34673                     col = i;
34674                     continue;
34675                 }
34676                 
34677                 col = Math.min(col, i);
34678                 
34679             }
34680             
34681             x = pos.x + col * (this.colWidth + this.padWidth);
34682             
34683             y = maxY[col];
34684             
34685             var positions = [];
34686             
34687             switch (box.length){
34688                 case 1 :
34689                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34690                     break;
34691                 case 2 :
34692                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34693                     break;
34694                 case 3 :
34695                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34696                     break;
34697                 case 4 :
34698                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34699                     break;
34700                 default :
34701                     break;
34702             }
34703             
34704             Roo.each(box, function(b,kk){
34705                 
34706                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34707                 
34708                 var sz = b.el.getSize();
34709                 
34710                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34711                 
34712             }, this);
34713             
34714         }, this);
34715         
34716         var mY = 0;
34717         
34718         for (var i = 0; i < this.cols; i++){
34719             mY = Math.max(mY, maxY[i]);
34720         }
34721         
34722         this.el.setHeight(mY - pos.y);
34723         
34724     },
34725     
34726 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34727 //    {
34728 //        var pos = this.el.getBox(true);
34729 //        var x = pos.x;
34730 //        var y = pos.y;
34731 //        var maxX = pos.right;
34732 //        
34733 //        var maxHeight = 0;
34734 //        
34735 //        Roo.each(items, function(item, k){
34736 //            
34737 //            var c = k % 2;
34738 //            
34739 //            item.el.position('absolute');
34740 //                
34741 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34742 //
34743 //            item.el.setWidth(width);
34744 //
34745 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34746 //
34747 //            item.el.setHeight(height);
34748 //            
34749 //            if(c == 0){
34750 //                item.el.setXY([x, y], isInstant ? false : true);
34751 //            } else {
34752 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34753 //            }
34754 //            
34755 //            y = y + height + this.alternativePadWidth;
34756 //            
34757 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34758 //            
34759 //        }, this);
34760 //        
34761 //        this.el.setHeight(maxHeight);
34762 //        
34763 //    },
34764     
34765     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34766     {
34767         var pos = this.el.getBox(true);
34768         
34769         var minX = pos.x;
34770         var minY = pos.y;
34771         
34772         var maxX = pos.right;
34773         
34774         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34775         
34776         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34777         
34778         Roo.each(queue, function(box, k){
34779             
34780             Roo.each(box, function(b, kk){
34781                 
34782                 b.el.position('absolute');
34783                 
34784                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34785                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34786                 
34787                 if(b.size == 'md-left' || b.size == 'md-right'){
34788                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34789                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34790                 }
34791                 
34792                 b.el.setWidth(width);
34793                 b.el.setHeight(height);
34794                 
34795             }, this);
34796             
34797             if(!box.length){
34798                 return;
34799             }
34800             
34801             var positions = [];
34802             
34803             switch (box.length){
34804                 case 1 :
34805                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34806                     break;
34807                 case 2 :
34808                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34809                     break;
34810                 case 3 :
34811                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34812                     break;
34813                 case 4 :
34814                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34815                     break;
34816                 default :
34817                     break;
34818             }
34819             
34820             Roo.each(box, function(b,kk){
34821                 
34822                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34823                 
34824                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34825                 
34826             }, this);
34827             
34828         }, this);
34829         
34830     },
34831     
34832     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34833     {
34834         Roo.each(eItems, function(b,k){
34835             
34836             b.size = (k == 0) ? 'sm' : 'xs';
34837             b.x = (k == 0) ? 2 : 1;
34838             b.y = (k == 0) ? 2 : 1;
34839             
34840             b.el.position('absolute');
34841             
34842             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34843                 
34844             b.el.setWidth(width);
34845             
34846             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34847             
34848             b.el.setHeight(height);
34849             
34850         }, this);
34851
34852         var positions = [];
34853         
34854         positions.push({
34855             x : maxX - this.unitWidth * 2 - this.gutter,
34856             y : minY
34857         });
34858         
34859         positions.push({
34860             x : maxX - this.unitWidth,
34861             y : minY + (this.unitWidth + this.gutter) * 2
34862         });
34863         
34864         positions.push({
34865             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34866             y : minY
34867         });
34868         
34869         Roo.each(eItems, function(b,k){
34870             
34871             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34872
34873         }, this);
34874         
34875     },
34876     
34877     getVerticalOneBoxColPositions : function(x, y, box)
34878     {
34879         var pos = [];
34880         
34881         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34882         
34883         if(box[0].size == 'md-left'){
34884             rand = 0;
34885         }
34886         
34887         if(box[0].size == 'md-right'){
34888             rand = 1;
34889         }
34890         
34891         pos.push({
34892             x : x + (this.unitWidth + this.gutter) * rand,
34893             y : y
34894         });
34895         
34896         return pos;
34897     },
34898     
34899     getVerticalTwoBoxColPositions : function(x, y, box)
34900     {
34901         var pos = [];
34902         
34903         if(box[0].size == 'xs'){
34904             
34905             pos.push({
34906                 x : x,
34907                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34908             });
34909
34910             pos.push({
34911                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34912                 y : y
34913             });
34914             
34915             return pos;
34916             
34917         }
34918         
34919         pos.push({
34920             x : x,
34921             y : y
34922         });
34923
34924         pos.push({
34925             x : x + (this.unitWidth + this.gutter) * 2,
34926             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34927         });
34928         
34929         return pos;
34930         
34931     },
34932     
34933     getVerticalThreeBoxColPositions : function(x, y, box)
34934     {
34935         var pos = [];
34936         
34937         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34938             
34939             pos.push({
34940                 x : x,
34941                 y : y
34942             });
34943
34944             pos.push({
34945                 x : x + (this.unitWidth + this.gutter) * 1,
34946                 y : y
34947             });
34948             
34949             pos.push({
34950                 x : x + (this.unitWidth + this.gutter) * 2,
34951                 y : y
34952             });
34953             
34954             return pos;
34955             
34956         }
34957         
34958         if(box[0].size == 'xs' && box[1].size == 'xs'){
34959             
34960             pos.push({
34961                 x : x,
34962                 y : y
34963             });
34964
34965             pos.push({
34966                 x : x,
34967                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34968             });
34969             
34970             pos.push({
34971                 x : x + (this.unitWidth + this.gutter) * 1,
34972                 y : y
34973             });
34974             
34975             return pos;
34976             
34977         }
34978         
34979         pos.push({
34980             x : x,
34981             y : y
34982         });
34983
34984         pos.push({
34985             x : x + (this.unitWidth + this.gutter) * 2,
34986             y : y
34987         });
34988
34989         pos.push({
34990             x : x + (this.unitWidth + this.gutter) * 2,
34991             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34992         });
34993             
34994         return pos;
34995         
34996     },
34997     
34998     getVerticalFourBoxColPositions : function(x, y, box)
34999     {
35000         var pos = [];
35001         
35002         if(box[0].size == 'xs'){
35003             
35004             pos.push({
35005                 x : x,
35006                 y : y
35007             });
35008
35009             pos.push({
35010                 x : x,
35011                 y : y + (this.unitHeight + this.gutter) * 1
35012             });
35013             
35014             pos.push({
35015                 x : x,
35016                 y : y + (this.unitHeight + this.gutter) * 2
35017             });
35018             
35019             pos.push({
35020                 x : x + (this.unitWidth + this.gutter) * 1,
35021                 y : y
35022             });
35023             
35024             return pos;
35025             
35026         }
35027         
35028         pos.push({
35029             x : x,
35030             y : y
35031         });
35032
35033         pos.push({
35034             x : x + (this.unitWidth + this.gutter) * 2,
35035             y : y
35036         });
35037
35038         pos.push({
35039             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35040             y : y + (this.unitHeight + this.gutter) * 1
35041         });
35042
35043         pos.push({
35044             x : x + (this.unitWidth + this.gutter) * 2,
35045             y : y + (this.unitWidth + this.gutter) * 2
35046         });
35047
35048         return pos;
35049         
35050     },
35051     
35052     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35053     {
35054         var pos = [];
35055         
35056         if(box[0].size == 'md-left'){
35057             pos.push({
35058                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35059                 y : minY
35060             });
35061             
35062             return pos;
35063         }
35064         
35065         if(box[0].size == 'md-right'){
35066             pos.push({
35067                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35068                 y : minY + (this.unitWidth + this.gutter) * 1
35069             });
35070             
35071             return pos;
35072         }
35073         
35074         var rand = Math.floor(Math.random() * (4 - box[0].y));
35075         
35076         pos.push({
35077             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35078             y : minY + (this.unitWidth + this.gutter) * rand
35079         });
35080         
35081         return pos;
35082         
35083     },
35084     
35085     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35086     {
35087         var pos = [];
35088         
35089         if(box[0].size == 'xs'){
35090             
35091             pos.push({
35092                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35093                 y : minY
35094             });
35095
35096             pos.push({
35097                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35098                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35099             });
35100             
35101             return pos;
35102             
35103         }
35104         
35105         pos.push({
35106             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35107             y : minY
35108         });
35109
35110         pos.push({
35111             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35112             y : minY + (this.unitWidth + this.gutter) * 2
35113         });
35114         
35115         return pos;
35116         
35117     },
35118     
35119     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35120     {
35121         var pos = [];
35122         
35123         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35124             
35125             pos.push({
35126                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35127                 y : minY
35128             });
35129
35130             pos.push({
35131                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35132                 y : minY + (this.unitWidth + this.gutter) * 1
35133             });
35134             
35135             pos.push({
35136                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35137                 y : minY + (this.unitWidth + this.gutter) * 2
35138             });
35139             
35140             return pos;
35141             
35142         }
35143         
35144         if(box[0].size == 'xs' && box[1].size == 'xs'){
35145             
35146             pos.push({
35147                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35148                 y : minY
35149             });
35150
35151             pos.push({
35152                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35153                 y : minY
35154             });
35155             
35156             pos.push({
35157                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35158                 y : minY + (this.unitWidth + this.gutter) * 1
35159             });
35160             
35161             return pos;
35162             
35163         }
35164         
35165         pos.push({
35166             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35167             y : minY
35168         });
35169
35170         pos.push({
35171             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35172             y : minY + (this.unitWidth + this.gutter) * 2
35173         });
35174
35175         pos.push({
35176             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35177             y : minY + (this.unitWidth + this.gutter) * 2
35178         });
35179             
35180         return pos;
35181         
35182     },
35183     
35184     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35185     {
35186         var pos = [];
35187         
35188         if(box[0].size == 'xs'){
35189             
35190             pos.push({
35191                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35192                 y : minY
35193             });
35194
35195             pos.push({
35196                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35197                 y : minY
35198             });
35199             
35200             pos.push({
35201                 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),
35202                 y : minY
35203             });
35204             
35205             pos.push({
35206                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35207                 y : minY + (this.unitWidth + this.gutter) * 1
35208             });
35209             
35210             return pos;
35211             
35212         }
35213         
35214         pos.push({
35215             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35216             y : minY
35217         });
35218         
35219         pos.push({
35220             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35221             y : minY + (this.unitWidth + this.gutter) * 2
35222         });
35223         
35224         pos.push({
35225             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35226             y : minY + (this.unitWidth + this.gutter) * 2
35227         });
35228         
35229         pos.push({
35230             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),
35231             y : minY + (this.unitWidth + this.gutter) * 2
35232         });
35233
35234         return pos;
35235         
35236     },
35237     
35238     /**
35239     * remove a Masonry Brick
35240     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35241     */
35242     removeBrick : function(brick_id)
35243     {
35244         if (!brick_id) {
35245             return;
35246         }
35247         
35248         for (var i = 0; i<this.bricks.length; i++) {
35249             if (this.bricks[i].id == brick_id) {
35250                 this.bricks.splice(i,1);
35251                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35252                 this.initial();
35253             }
35254         }
35255     },
35256     
35257     /**
35258     * adds a Masonry Brick
35259     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35260     */
35261     addBrick : function(cfg)
35262     {
35263         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35264         //this.register(cn);
35265         cn.parentId = this.id;
35266         cn.render(this.el);
35267         return cn;
35268     },
35269     
35270     /**
35271     * register a Masonry Brick
35272     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35273     */
35274     
35275     register : function(brick)
35276     {
35277         this.bricks.push(brick);
35278         brick.masonryId = this.id;
35279     },
35280     
35281     /**
35282     * clear all the Masonry Brick
35283     */
35284     clearAll : function()
35285     {
35286         this.bricks = [];
35287         //this.getChildContainer().dom.innerHTML = "";
35288         this.el.dom.innerHTML = '';
35289     },
35290     
35291     getSelected : function()
35292     {
35293         if (!this.selectedBrick) {
35294             return false;
35295         }
35296         
35297         return this.selectedBrick;
35298     }
35299 });
35300
35301 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35302     
35303     groups: {},
35304      /**
35305     * register a Masonry Layout
35306     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35307     */
35308     
35309     register : function(layout)
35310     {
35311         this.groups[layout.id] = layout;
35312     },
35313     /**
35314     * fetch a  Masonry Layout based on the masonry layout ID
35315     * @param {string} the masonry layout to add
35316     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35317     */
35318     
35319     get: function(layout_id) {
35320         if (typeof(this.groups[layout_id]) == 'undefined') {
35321             return false;
35322         }
35323         return this.groups[layout_id] ;
35324     }
35325     
35326     
35327     
35328 });
35329
35330  
35331
35332  /**
35333  *
35334  * This is based on 
35335  * http://masonry.desandro.com
35336  *
35337  * The idea is to render all the bricks based on vertical width...
35338  *
35339  * The original code extends 'outlayer' - we might need to use that....
35340  * 
35341  */
35342
35343
35344 /**
35345  * @class Roo.bootstrap.LayoutMasonryAuto
35346  * @extends Roo.bootstrap.Component
35347  * Bootstrap Layout Masonry class
35348  * 
35349  * @constructor
35350  * Create a new Element
35351  * @param {Object} config The config object
35352  */
35353
35354 Roo.bootstrap.LayoutMasonryAuto = function(config){
35355     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35356 };
35357
35358 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35359     
35360       /**
35361      * @cfg {Boolean} isFitWidth  - resize the width..
35362      */   
35363     isFitWidth : false,  // options..
35364     /**
35365      * @cfg {Boolean} isOriginLeft = left align?
35366      */   
35367     isOriginLeft : true,
35368     /**
35369      * @cfg {Boolean} isOriginTop = top align?
35370      */   
35371     isOriginTop : false,
35372     /**
35373      * @cfg {Boolean} isLayoutInstant = no animation?
35374      */   
35375     isLayoutInstant : false, // needed?
35376     /**
35377      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35378      */   
35379     isResizingContainer : true,
35380     /**
35381      * @cfg {Number} columnWidth  width of the columns 
35382      */   
35383     
35384     columnWidth : 0,
35385     
35386     /**
35387      * @cfg {Number} maxCols maximum number of columns
35388      */   
35389     
35390     maxCols: 0,
35391     /**
35392      * @cfg {Number} padHeight padding below box..
35393      */   
35394     
35395     padHeight : 10, 
35396     
35397     /**
35398      * @cfg {Boolean} isAutoInitial defalut true
35399      */   
35400     
35401     isAutoInitial : true, 
35402     
35403     // private?
35404     gutter : 0,
35405     
35406     containerWidth: 0,
35407     initialColumnWidth : 0,
35408     currentSize : null,
35409     
35410     colYs : null, // array.
35411     maxY : 0,
35412     padWidth: 10,
35413     
35414     
35415     tag: 'div',
35416     cls: '',
35417     bricks: null, //CompositeElement
35418     cols : 0, // array?
35419     // element : null, // wrapped now this.el
35420     _isLayoutInited : null, 
35421     
35422     
35423     getAutoCreate : function(){
35424         
35425         var cfg = {
35426             tag: this.tag,
35427             cls: 'blog-masonary-wrapper ' + this.cls,
35428             cn : {
35429                 cls : 'mas-boxes masonary'
35430             }
35431         };
35432         
35433         return cfg;
35434     },
35435     
35436     getChildContainer: function( )
35437     {
35438         if (this.boxesEl) {
35439             return this.boxesEl;
35440         }
35441         
35442         this.boxesEl = this.el.select('.mas-boxes').first();
35443         
35444         return this.boxesEl;
35445     },
35446     
35447     
35448     initEvents : function()
35449     {
35450         var _this = this;
35451         
35452         if(this.isAutoInitial){
35453             Roo.log('hook children rendered');
35454             this.on('childrenrendered', function() {
35455                 Roo.log('children rendered');
35456                 _this.initial();
35457             } ,this);
35458         }
35459         
35460     },
35461     
35462     initial : function()
35463     {
35464         this.reloadItems();
35465
35466         this.currentSize = this.el.getBox(true);
35467
35468         /// was window resize... - let's see if this works..
35469         Roo.EventManager.onWindowResize(this.resize, this); 
35470
35471         if(!this.isAutoInitial){
35472             this.layout();
35473             return;
35474         }
35475         
35476         this.layout.defer(500,this);
35477     },
35478     
35479     reloadItems: function()
35480     {
35481         this.bricks = this.el.select('.masonry-brick', true);
35482         
35483         this.bricks.each(function(b) {
35484             //Roo.log(b.getSize());
35485             if (!b.attr('originalwidth')) {
35486                 b.attr('originalwidth',  b.getSize().width);
35487             }
35488             
35489         });
35490         
35491         Roo.log(this.bricks.elements.length);
35492     },
35493     
35494     resize : function()
35495     {
35496         Roo.log('resize');
35497         var cs = this.el.getBox(true);
35498         
35499         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35500             Roo.log("no change in with or X");
35501             return;
35502         }
35503         this.currentSize = cs;
35504         this.layout();
35505     },
35506     
35507     layout : function()
35508     {
35509          Roo.log('layout');
35510         this._resetLayout();
35511         //this._manageStamps();
35512       
35513         // don't animate first layout
35514         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35515         this.layoutItems( isInstant );
35516       
35517         // flag for initalized
35518         this._isLayoutInited = true;
35519     },
35520     
35521     layoutItems : function( isInstant )
35522     {
35523         //var items = this._getItemsForLayout( this.items );
35524         // original code supports filtering layout items.. we just ignore it..
35525         
35526         this._layoutItems( this.bricks , isInstant );
35527       
35528         this._postLayout();
35529     },
35530     _layoutItems : function ( items , isInstant)
35531     {
35532        //this.fireEvent( 'layout', this, items );
35533     
35534
35535         if ( !items || !items.elements.length ) {
35536           // no items, emit event with empty array
35537             return;
35538         }
35539
35540         var queue = [];
35541         items.each(function(item) {
35542             Roo.log("layout item");
35543             Roo.log(item);
35544             // get x/y object from method
35545             var position = this._getItemLayoutPosition( item );
35546             // enqueue
35547             position.item = item;
35548             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35549             queue.push( position );
35550         }, this);
35551       
35552         this._processLayoutQueue( queue );
35553     },
35554     /** Sets position of item in DOM
35555     * @param {Element} item
35556     * @param {Number} x - horizontal position
35557     * @param {Number} y - vertical position
35558     * @param {Boolean} isInstant - disables transitions
35559     */
35560     _processLayoutQueue : function( queue )
35561     {
35562         for ( var i=0, len = queue.length; i < len; i++ ) {
35563             var obj = queue[i];
35564             obj.item.position('absolute');
35565             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35566         }
35567     },
35568       
35569     
35570     /**
35571     * Any logic you want to do after each layout,
35572     * i.e. size the container
35573     */
35574     _postLayout : function()
35575     {
35576         this.resizeContainer();
35577     },
35578     
35579     resizeContainer : function()
35580     {
35581         if ( !this.isResizingContainer ) {
35582             return;
35583         }
35584         var size = this._getContainerSize();
35585         if ( size ) {
35586             this.el.setSize(size.width,size.height);
35587             this.boxesEl.setSize(size.width,size.height);
35588         }
35589     },
35590     
35591     
35592     
35593     _resetLayout : function()
35594     {
35595         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35596         this.colWidth = this.el.getWidth();
35597         //this.gutter = this.el.getWidth(); 
35598         
35599         this.measureColumns();
35600
35601         // reset column Y
35602         var i = this.cols;
35603         this.colYs = [];
35604         while (i--) {
35605             this.colYs.push( 0 );
35606         }
35607     
35608         this.maxY = 0;
35609     },
35610
35611     measureColumns : function()
35612     {
35613         this.getContainerWidth();
35614       // if columnWidth is 0, default to outerWidth of first item
35615         if ( !this.columnWidth ) {
35616             var firstItem = this.bricks.first();
35617             Roo.log(firstItem);
35618             this.columnWidth  = this.containerWidth;
35619             if (firstItem && firstItem.attr('originalwidth') ) {
35620                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35621             }
35622             // columnWidth fall back to item of first element
35623             Roo.log("set column width?");
35624                         this.initialColumnWidth = this.columnWidth  ;
35625
35626             // if first elem has no width, default to size of container
35627             
35628         }
35629         
35630         
35631         if (this.initialColumnWidth) {
35632             this.columnWidth = this.initialColumnWidth;
35633         }
35634         
35635         
35636             
35637         // column width is fixed at the top - however if container width get's smaller we should
35638         // reduce it...
35639         
35640         // this bit calcs how man columns..
35641             
35642         var columnWidth = this.columnWidth += this.gutter;
35643       
35644         // calculate columns
35645         var containerWidth = this.containerWidth + this.gutter;
35646         
35647         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35648         // fix rounding errors, typically with gutters
35649         var excess = columnWidth - containerWidth % columnWidth;
35650         
35651         
35652         // if overshoot is less than a pixel, round up, otherwise floor it
35653         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35654         cols = Math[ mathMethod ]( cols );
35655         this.cols = Math.max( cols, 1 );
35656         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35657         
35658          // padding positioning..
35659         var totalColWidth = this.cols * this.columnWidth;
35660         var padavail = this.containerWidth - totalColWidth;
35661         // so for 2 columns - we need 3 'pads'
35662         
35663         var padNeeded = (1+this.cols) * this.padWidth;
35664         
35665         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35666         
35667         this.columnWidth += padExtra
35668         //this.padWidth = Math.floor(padavail /  ( this.cols));
35669         
35670         // adjust colum width so that padding is fixed??
35671         
35672         // we have 3 columns ... total = width * 3
35673         // we have X left over... that should be used by 
35674         
35675         //if (this.expandC) {
35676             
35677         //}
35678         
35679         
35680         
35681     },
35682     
35683     getContainerWidth : function()
35684     {
35685        /* // container is parent if fit width
35686         var container = this.isFitWidth ? this.element.parentNode : this.element;
35687         // check that this.size and size are there
35688         // IE8 triggers resize on body size change, so they might not be
35689         
35690         var size = getSize( container );  //FIXME
35691         this.containerWidth = size && size.innerWidth; //FIXME
35692         */
35693          
35694         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35695         
35696     },
35697     
35698     _getItemLayoutPosition : function( item )  // what is item?
35699     {
35700         // we resize the item to our columnWidth..
35701       
35702         item.setWidth(this.columnWidth);
35703         item.autoBoxAdjust  = false;
35704         
35705         var sz = item.getSize();
35706  
35707         // how many columns does this brick span
35708         var remainder = this.containerWidth % this.columnWidth;
35709         
35710         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35711         // round if off by 1 pixel, otherwise use ceil
35712         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35713         colSpan = Math.min( colSpan, this.cols );
35714         
35715         // normally this should be '1' as we dont' currently allow multi width columns..
35716         
35717         var colGroup = this._getColGroup( colSpan );
35718         // get the minimum Y value from the columns
35719         var minimumY = Math.min.apply( Math, colGroup );
35720         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35721         
35722         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35723          
35724         // position the brick
35725         var position = {
35726             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35727             y: this.currentSize.y + minimumY + this.padHeight
35728         };
35729         
35730         Roo.log(position);
35731         // apply setHeight to necessary columns
35732         var setHeight = minimumY + sz.height + this.padHeight;
35733         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35734         
35735         var setSpan = this.cols + 1 - colGroup.length;
35736         for ( var i = 0; i < setSpan; i++ ) {
35737           this.colYs[ shortColIndex + i ] = setHeight ;
35738         }
35739       
35740         return position;
35741     },
35742     
35743     /**
35744      * @param {Number} colSpan - number of columns the element spans
35745      * @returns {Array} colGroup
35746      */
35747     _getColGroup : function( colSpan )
35748     {
35749         if ( colSpan < 2 ) {
35750           // if brick spans only one column, use all the column Ys
35751           return this.colYs;
35752         }
35753       
35754         var colGroup = [];
35755         // how many different places could this brick fit horizontally
35756         var groupCount = this.cols + 1 - colSpan;
35757         // for each group potential horizontal position
35758         for ( var i = 0; i < groupCount; i++ ) {
35759           // make an array of colY values for that one group
35760           var groupColYs = this.colYs.slice( i, i + colSpan );
35761           // and get the max value of the array
35762           colGroup[i] = Math.max.apply( Math, groupColYs );
35763         }
35764         return colGroup;
35765     },
35766     /*
35767     _manageStamp : function( stamp )
35768     {
35769         var stampSize =  stamp.getSize();
35770         var offset = stamp.getBox();
35771         // get the columns that this stamp affects
35772         var firstX = this.isOriginLeft ? offset.x : offset.right;
35773         var lastX = firstX + stampSize.width;
35774         var firstCol = Math.floor( firstX / this.columnWidth );
35775         firstCol = Math.max( 0, firstCol );
35776         
35777         var lastCol = Math.floor( lastX / this.columnWidth );
35778         // lastCol should not go over if multiple of columnWidth #425
35779         lastCol -= lastX % this.columnWidth ? 0 : 1;
35780         lastCol = Math.min( this.cols - 1, lastCol );
35781         
35782         // set colYs to bottom of the stamp
35783         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35784             stampSize.height;
35785             
35786         for ( var i = firstCol; i <= lastCol; i++ ) {
35787           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35788         }
35789     },
35790     */
35791     
35792     _getContainerSize : function()
35793     {
35794         this.maxY = Math.max.apply( Math, this.colYs );
35795         var size = {
35796             height: this.maxY
35797         };
35798       
35799         if ( this.isFitWidth ) {
35800             size.width = this._getContainerFitWidth();
35801         }
35802       
35803         return size;
35804     },
35805     
35806     _getContainerFitWidth : function()
35807     {
35808         var unusedCols = 0;
35809         // count unused columns
35810         var i = this.cols;
35811         while ( --i ) {
35812           if ( this.colYs[i] !== 0 ) {
35813             break;
35814           }
35815           unusedCols++;
35816         }
35817         // fit container to columns that have been used
35818         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35819     },
35820     
35821     needsResizeLayout : function()
35822     {
35823         var previousWidth = this.containerWidth;
35824         this.getContainerWidth();
35825         return previousWidth !== this.containerWidth;
35826     }
35827  
35828 });
35829
35830  
35831
35832  /*
35833  * - LGPL
35834  *
35835  * element
35836  * 
35837  */
35838
35839 /**
35840  * @class Roo.bootstrap.MasonryBrick
35841  * @extends Roo.bootstrap.Component
35842  * Bootstrap MasonryBrick class
35843  * 
35844  * @constructor
35845  * Create a new MasonryBrick
35846  * @param {Object} config The config object
35847  */
35848
35849 Roo.bootstrap.MasonryBrick = function(config){
35850     
35851     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35852     
35853     Roo.bootstrap.MasonryBrick.register(this);
35854     
35855     this.addEvents({
35856         // raw events
35857         /**
35858          * @event click
35859          * When a MasonryBrick is clcik
35860          * @param {Roo.bootstrap.MasonryBrick} this
35861          * @param {Roo.EventObject} e
35862          */
35863         "click" : true
35864     });
35865 };
35866
35867 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35868     
35869     /**
35870      * @cfg {String} title
35871      */   
35872     title : '',
35873     /**
35874      * @cfg {String} html
35875      */   
35876     html : '',
35877     /**
35878      * @cfg {String} bgimage
35879      */   
35880     bgimage : '',
35881     /**
35882      * @cfg {String} videourl
35883      */   
35884     videourl : '',
35885     /**
35886      * @cfg {String} cls
35887      */   
35888     cls : '',
35889     /**
35890      * @cfg {String} href
35891      */   
35892     href : '',
35893     /**
35894      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35895      */   
35896     size : 'xs',
35897     
35898     /**
35899      * @cfg {String} placetitle (center|bottom)
35900      */   
35901     placetitle : '',
35902     
35903     /**
35904      * @cfg {Boolean} isFitContainer defalut true
35905      */   
35906     isFitContainer : true, 
35907     
35908     /**
35909      * @cfg {Boolean} preventDefault defalut false
35910      */   
35911     preventDefault : false, 
35912     
35913     /**
35914      * @cfg {Boolean} inverse defalut false
35915      */   
35916     maskInverse : false, 
35917     
35918     getAutoCreate : function()
35919     {
35920         if(!this.isFitContainer){
35921             return this.getSplitAutoCreate();
35922         }
35923         
35924         var cls = 'masonry-brick masonry-brick-full';
35925         
35926         if(this.href.length){
35927             cls += ' masonry-brick-link';
35928         }
35929         
35930         if(this.bgimage.length){
35931             cls += ' masonry-brick-image';
35932         }
35933         
35934         if(this.maskInverse){
35935             cls += ' mask-inverse';
35936         }
35937         
35938         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35939             cls += ' enable-mask';
35940         }
35941         
35942         if(this.size){
35943             cls += ' masonry-' + this.size + '-brick';
35944         }
35945         
35946         if(this.placetitle.length){
35947             
35948             switch (this.placetitle) {
35949                 case 'center' :
35950                     cls += ' masonry-center-title';
35951                     break;
35952                 case 'bottom' :
35953                     cls += ' masonry-bottom-title';
35954                     break;
35955                 default:
35956                     break;
35957             }
35958             
35959         } else {
35960             if(!this.html.length && !this.bgimage.length){
35961                 cls += ' masonry-center-title';
35962             }
35963
35964             if(!this.html.length && this.bgimage.length){
35965                 cls += ' masonry-bottom-title';
35966             }
35967         }
35968         
35969         if(this.cls){
35970             cls += ' ' + this.cls;
35971         }
35972         
35973         var cfg = {
35974             tag: (this.href.length) ? 'a' : 'div',
35975             cls: cls,
35976             cn: [
35977                 {
35978                     tag: 'div',
35979                     cls: 'masonry-brick-mask'
35980                 },
35981                 {
35982                     tag: 'div',
35983                     cls: 'masonry-brick-paragraph',
35984                     cn: []
35985                 }
35986             ]
35987         };
35988         
35989         if(this.href.length){
35990             cfg.href = this.href;
35991         }
35992         
35993         var cn = cfg.cn[1].cn;
35994         
35995         if(this.title.length){
35996             cn.push({
35997                 tag: 'h4',
35998                 cls: 'masonry-brick-title',
35999                 html: this.title
36000             });
36001         }
36002         
36003         if(this.html.length){
36004             cn.push({
36005                 tag: 'p',
36006                 cls: 'masonry-brick-text',
36007                 html: this.html
36008             });
36009         }
36010         
36011         if (!this.title.length && !this.html.length) {
36012             cfg.cn[1].cls += ' hide';
36013         }
36014         
36015         if(this.bgimage.length){
36016             cfg.cn.push({
36017                 tag: 'img',
36018                 cls: 'masonry-brick-image-view',
36019                 src: this.bgimage
36020             });
36021         }
36022         
36023         if(this.videourl.length){
36024             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36025             // youtube support only?
36026             cfg.cn.push({
36027                 tag: 'iframe',
36028                 cls: 'masonry-brick-image-view',
36029                 src: vurl,
36030                 frameborder : 0,
36031                 allowfullscreen : true
36032             });
36033         }
36034         
36035         return cfg;
36036         
36037     },
36038     
36039     getSplitAutoCreate : function()
36040     {
36041         var cls = 'masonry-brick masonry-brick-split';
36042         
36043         if(this.href.length){
36044             cls += ' masonry-brick-link';
36045         }
36046         
36047         if(this.bgimage.length){
36048             cls += ' masonry-brick-image';
36049         }
36050         
36051         if(this.size){
36052             cls += ' masonry-' + this.size + '-brick';
36053         }
36054         
36055         switch (this.placetitle) {
36056             case 'center' :
36057                 cls += ' masonry-center-title';
36058                 break;
36059             case 'bottom' :
36060                 cls += ' masonry-bottom-title';
36061                 break;
36062             default:
36063                 if(!this.bgimage.length){
36064                     cls += ' masonry-center-title';
36065                 }
36066
36067                 if(this.bgimage.length){
36068                     cls += ' masonry-bottom-title';
36069                 }
36070                 break;
36071         }
36072         
36073         if(this.cls){
36074             cls += ' ' + this.cls;
36075         }
36076         
36077         var cfg = {
36078             tag: (this.href.length) ? 'a' : 'div',
36079             cls: cls,
36080             cn: [
36081                 {
36082                     tag: 'div',
36083                     cls: 'masonry-brick-split-head',
36084                     cn: [
36085                         {
36086                             tag: 'div',
36087                             cls: 'masonry-brick-paragraph',
36088                             cn: []
36089                         }
36090                     ]
36091                 },
36092                 {
36093                     tag: 'div',
36094                     cls: 'masonry-brick-split-body',
36095                     cn: []
36096                 }
36097             ]
36098         };
36099         
36100         if(this.href.length){
36101             cfg.href = this.href;
36102         }
36103         
36104         if(this.title.length){
36105             cfg.cn[0].cn[0].cn.push({
36106                 tag: 'h4',
36107                 cls: 'masonry-brick-title',
36108                 html: this.title
36109             });
36110         }
36111         
36112         if(this.html.length){
36113             cfg.cn[1].cn.push({
36114                 tag: 'p',
36115                 cls: 'masonry-brick-text',
36116                 html: this.html
36117             });
36118         }
36119
36120         if(this.bgimage.length){
36121             cfg.cn[0].cn.push({
36122                 tag: 'img',
36123                 cls: 'masonry-brick-image-view',
36124                 src: this.bgimage
36125             });
36126         }
36127         
36128         if(this.videourl.length){
36129             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36130             // youtube support only?
36131             cfg.cn[0].cn.cn.push({
36132                 tag: 'iframe',
36133                 cls: 'masonry-brick-image-view',
36134                 src: vurl,
36135                 frameborder : 0,
36136                 allowfullscreen : true
36137             });
36138         }
36139         
36140         return cfg;
36141     },
36142     
36143     initEvents: function() 
36144     {
36145         switch (this.size) {
36146             case 'xs' :
36147                 this.x = 1;
36148                 this.y = 1;
36149                 break;
36150             case 'sm' :
36151                 this.x = 2;
36152                 this.y = 2;
36153                 break;
36154             case 'md' :
36155             case 'md-left' :
36156             case 'md-right' :
36157                 this.x = 3;
36158                 this.y = 3;
36159                 break;
36160             case 'tall' :
36161                 this.x = 2;
36162                 this.y = 3;
36163                 break;
36164             case 'wide' :
36165                 this.x = 3;
36166                 this.y = 2;
36167                 break;
36168             case 'wide-thin' :
36169                 this.x = 3;
36170                 this.y = 1;
36171                 break;
36172                         
36173             default :
36174                 break;
36175         }
36176         
36177         if(Roo.isTouch){
36178             this.el.on('touchstart', this.onTouchStart, this);
36179             this.el.on('touchmove', this.onTouchMove, this);
36180             this.el.on('touchend', this.onTouchEnd, this);
36181             this.el.on('contextmenu', this.onContextMenu, this);
36182         } else {
36183             this.el.on('mouseenter'  ,this.enter, this);
36184             this.el.on('mouseleave', this.leave, this);
36185             this.el.on('click', this.onClick, this);
36186         }
36187         
36188         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36189             this.parent().bricks.push(this);   
36190         }
36191         
36192     },
36193     
36194     onClick: function(e, el)
36195     {
36196         var time = this.endTimer - this.startTimer;
36197         // Roo.log(e.preventDefault());
36198         if(Roo.isTouch){
36199             if(time > 1000){
36200                 e.preventDefault();
36201                 return;
36202             }
36203         }
36204         
36205         if(!this.preventDefault){
36206             return;
36207         }
36208         
36209         e.preventDefault();
36210         
36211         if (this.activeClass != '') {
36212             this.selectBrick();
36213         }
36214         
36215         this.fireEvent('click', this, e);
36216     },
36217     
36218     enter: function(e, el)
36219     {
36220         e.preventDefault();
36221         
36222         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36223             return;
36224         }
36225         
36226         if(this.bgimage.length && this.html.length){
36227             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36228         }
36229     },
36230     
36231     leave: function(e, el)
36232     {
36233         e.preventDefault();
36234         
36235         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36236             return;
36237         }
36238         
36239         if(this.bgimage.length && this.html.length){
36240             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36241         }
36242     },
36243     
36244     onTouchStart: function(e, el)
36245     {
36246 //        e.preventDefault();
36247         
36248         this.touchmoved = false;
36249         
36250         if(!this.isFitContainer){
36251             return;
36252         }
36253         
36254         if(!this.bgimage.length || !this.html.length){
36255             return;
36256         }
36257         
36258         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36259         
36260         this.timer = new Date().getTime();
36261         
36262     },
36263     
36264     onTouchMove: function(e, el)
36265     {
36266         this.touchmoved = true;
36267     },
36268     
36269     onContextMenu : function(e,el)
36270     {
36271         e.preventDefault();
36272         e.stopPropagation();
36273         return false;
36274     },
36275     
36276     onTouchEnd: function(e, el)
36277     {
36278 //        e.preventDefault();
36279         
36280         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36281         
36282             this.leave(e,el);
36283             
36284             return;
36285         }
36286         
36287         if(!this.bgimage.length || !this.html.length){
36288             
36289             if(this.href.length){
36290                 window.location.href = this.href;
36291             }
36292             
36293             return;
36294         }
36295         
36296         if(!this.isFitContainer){
36297             return;
36298         }
36299         
36300         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36301         
36302         window.location.href = this.href;
36303     },
36304     
36305     //selection on single brick only
36306     selectBrick : function() {
36307         
36308         if (!this.parentId) {
36309             return;
36310         }
36311         
36312         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36313         var index = m.selectedBrick.indexOf(this.id);
36314         
36315         if ( index > -1) {
36316             m.selectedBrick.splice(index,1);
36317             this.el.removeClass(this.activeClass);
36318             return;
36319         }
36320         
36321         for(var i = 0; i < m.selectedBrick.length; i++) {
36322             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36323             b.el.removeClass(b.activeClass);
36324         }
36325         
36326         m.selectedBrick = [];
36327         
36328         m.selectedBrick.push(this.id);
36329         this.el.addClass(this.activeClass);
36330         return;
36331     },
36332     
36333     isSelected : function(){
36334         return this.el.hasClass(this.activeClass);
36335         
36336     }
36337 });
36338
36339 Roo.apply(Roo.bootstrap.MasonryBrick, {
36340     
36341     //groups: {},
36342     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36343      /**
36344     * register a Masonry Brick
36345     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36346     */
36347     
36348     register : function(brick)
36349     {
36350         //this.groups[brick.id] = brick;
36351         this.groups.add(brick.id, brick);
36352     },
36353     /**
36354     * fetch a  masonry brick based on the masonry brick ID
36355     * @param {string} the masonry brick to add
36356     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36357     */
36358     
36359     get: function(brick_id) 
36360     {
36361         // if (typeof(this.groups[brick_id]) == 'undefined') {
36362         //     return false;
36363         // }
36364         // return this.groups[brick_id] ;
36365         
36366         if(this.groups.key(brick_id)) {
36367             return this.groups.key(brick_id);
36368         }
36369         
36370         return false;
36371     }
36372     
36373     
36374     
36375 });
36376
36377  /*
36378  * - LGPL
36379  *
36380  * element
36381  * 
36382  */
36383
36384 /**
36385  * @class Roo.bootstrap.Brick
36386  * @extends Roo.bootstrap.Component
36387  * Bootstrap Brick class
36388  * 
36389  * @constructor
36390  * Create a new Brick
36391  * @param {Object} config The config object
36392  */
36393
36394 Roo.bootstrap.Brick = function(config){
36395     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36396     
36397     this.addEvents({
36398         // raw events
36399         /**
36400          * @event click
36401          * When a Brick is click
36402          * @param {Roo.bootstrap.Brick} this
36403          * @param {Roo.EventObject} e
36404          */
36405         "click" : true
36406     });
36407 };
36408
36409 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36410     
36411     /**
36412      * @cfg {String} title
36413      */   
36414     title : '',
36415     /**
36416      * @cfg {String} html
36417      */   
36418     html : '',
36419     /**
36420      * @cfg {String} bgimage
36421      */   
36422     bgimage : '',
36423     /**
36424      * @cfg {String} cls
36425      */   
36426     cls : '',
36427     /**
36428      * @cfg {String} href
36429      */   
36430     href : '',
36431     /**
36432      * @cfg {String} video
36433      */   
36434     video : '',
36435     /**
36436      * @cfg {Boolean} square
36437      */   
36438     square : true,
36439     
36440     getAutoCreate : function()
36441     {
36442         var cls = 'roo-brick';
36443         
36444         if(this.href.length){
36445             cls += ' roo-brick-link';
36446         }
36447         
36448         if(this.bgimage.length){
36449             cls += ' roo-brick-image';
36450         }
36451         
36452         if(!this.html.length && !this.bgimage.length){
36453             cls += ' roo-brick-center-title';
36454         }
36455         
36456         if(!this.html.length && this.bgimage.length){
36457             cls += ' roo-brick-bottom-title';
36458         }
36459         
36460         if(this.cls){
36461             cls += ' ' + this.cls;
36462         }
36463         
36464         var cfg = {
36465             tag: (this.href.length) ? 'a' : 'div',
36466             cls: cls,
36467             cn: [
36468                 {
36469                     tag: 'div',
36470                     cls: 'roo-brick-paragraph',
36471                     cn: []
36472                 }
36473             ]
36474         };
36475         
36476         if(this.href.length){
36477             cfg.href = this.href;
36478         }
36479         
36480         var cn = cfg.cn[0].cn;
36481         
36482         if(this.title.length){
36483             cn.push({
36484                 tag: 'h4',
36485                 cls: 'roo-brick-title',
36486                 html: this.title
36487             });
36488         }
36489         
36490         if(this.html.length){
36491             cn.push({
36492                 tag: 'p',
36493                 cls: 'roo-brick-text',
36494                 html: this.html
36495             });
36496         } else {
36497             cn.cls += ' hide';
36498         }
36499         
36500         if(this.bgimage.length){
36501             cfg.cn.push({
36502                 tag: 'img',
36503                 cls: 'roo-brick-image-view',
36504                 src: this.bgimage
36505             });
36506         }
36507         
36508         return cfg;
36509     },
36510     
36511     initEvents: function() 
36512     {
36513         if(this.title.length || this.html.length){
36514             this.el.on('mouseenter'  ,this.enter, this);
36515             this.el.on('mouseleave', this.leave, this);
36516         }
36517         
36518         Roo.EventManager.onWindowResize(this.resize, this); 
36519         
36520         if(this.bgimage.length){
36521             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36522             this.imageEl.on('load', this.onImageLoad, this);
36523             return;
36524         }
36525         
36526         this.resize();
36527     },
36528     
36529     onImageLoad : function()
36530     {
36531         this.resize();
36532     },
36533     
36534     resize : function()
36535     {
36536         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36537         
36538         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36539         
36540         if(this.bgimage.length){
36541             var image = this.el.select('.roo-brick-image-view', true).first();
36542             
36543             image.setWidth(paragraph.getWidth());
36544             
36545             if(this.square){
36546                 image.setHeight(paragraph.getWidth());
36547             }
36548             
36549             this.el.setHeight(image.getHeight());
36550             paragraph.setHeight(image.getHeight());
36551             
36552         }
36553         
36554     },
36555     
36556     enter: function(e, el)
36557     {
36558         e.preventDefault();
36559         
36560         if(this.bgimage.length){
36561             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36562             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36563         }
36564     },
36565     
36566     leave: function(e, el)
36567     {
36568         e.preventDefault();
36569         
36570         if(this.bgimage.length){
36571             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36572             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36573         }
36574     }
36575     
36576 });
36577
36578  
36579
36580  /*
36581  * - LGPL
36582  *
36583  * Number field 
36584  */
36585
36586 /**
36587  * @class Roo.bootstrap.NumberField
36588  * @extends Roo.bootstrap.Input
36589  * Bootstrap NumberField class
36590  * 
36591  * 
36592  * 
36593  * 
36594  * @constructor
36595  * Create a new NumberField
36596  * @param {Object} config The config object
36597  */
36598
36599 Roo.bootstrap.NumberField = function(config){
36600     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36601 };
36602
36603 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36604     
36605     /**
36606      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36607      */
36608     allowDecimals : true,
36609     /**
36610      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36611      */
36612     decimalSeparator : ".",
36613     /**
36614      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36615      */
36616     decimalPrecision : 2,
36617     /**
36618      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36619      */
36620     allowNegative : true,
36621     
36622     /**
36623      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36624      */
36625     allowZero: true,
36626     /**
36627      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36628      */
36629     minValue : Number.NEGATIVE_INFINITY,
36630     /**
36631      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36632      */
36633     maxValue : Number.MAX_VALUE,
36634     /**
36635      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36636      */
36637     minText : "The minimum value for this field is {0}",
36638     /**
36639      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36640      */
36641     maxText : "The maximum value for this field is {0}",
36642     /**
36643      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36644      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36645      */
36646     nanText : "{0} is not a valid number",
36647     /**
36648      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36649      */
36650     thousandsDelimiter : false,
36651     /**
36652      * @cfg {String} valueAlign alignment of value
36653      */
36654     valueAlign : "left",
36655
36656     getAutoCreate : function()
36657     {
36658         var hiddenInput = {
36659             tag: 'input',
36660             type: 'hidden',
36661             id: Roo.id(),
36662             cls: 'hidden-number-input'
36663         };
36664         
36665         if (this.name) {
36666             hiddenInput.name = this.name;
36667         }
36668         
36669         this.name = '';
36670         
36671         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36672         
36673         this.name = hiddenInput.name;
36674         
36675         if(cfg.cn.length > 0) {
36676             cfg.cn.push(hiddenInput);
36677         }
36678         
36679         return cfg;
36680     },
36681
36682     // private
36683     initEvents : function()
36684     {   
36685         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36686         
36687         var allowed = "0123456789";
36688         
36689         if(this.allowDecimals){
36690             allowed += this.decimalSeparator;
36691         }
36692         
36693         if(this.allowNegative){
36694             allowed += "-";
36695         }
36696         
36697         if(this.thousandsDelimiter) {
36698             allowed += ",";
36699         }
36700         
36701         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36702         
36703         var keyPress = function(e){
36704             
36705             var k = e.getKey();
36706             
36707             var c = e.getCharCode();
36708             
36709             if(
36710                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36711                     allowed.indexOf(String.fromCharCode(c)) === -1
36712             ){
36713                 e.stopEvent();
36714                 return;
36715             }
36716             
36717             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36718                 return;
36719             }
36720             
36721             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36722                 e.stopEvent();
36723             }
36724         };
36725         
36726         this.el.on("keypress", keyPress, this);
36727     },
36728     
36729     validateValue : function(value)
36730     {
36731         
36732         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36733             return false;
36734         }
36735         
36736         var num = this.parseValue(value);
36737         
36738         if(isNaN(num)){
36739             this.markInvalid(String.format(this.nanText, value));
36740             return false;
36741         }
36742         
36743         if(num < this.minValue){
36744             this.markInvalid(String.format(this.minText, this.minValue));
36745             return false;
36746         }
36747         
36748         if(num > this.maxValue){
36749             this.markInvalid(String.format(this.maxText, this.maxValue));
36750             return false;
36751         }
36752         
36753         return true;
36754     },
36755
36756     getValue : function()
36757     {
36758         var v = this.hiddenEl().getValue();
36759         
36760         return this.fixPrecision(this.parseValue(v));
36761     },
36762
36763     parseValue : function(value)
36764     {
36765         if(this.thousandsDelimiter) {
36766             value += "";
36767             r = new RegExp(",", "g");
36768             value = value.replace(r, "");
36769         }
36770         
36771         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36772         return isNaN(value) ? '' : value;
36773     },
36774
36775     fixPrecision : function(value)
36776     {
36777         if(this.thousandsDelimiter) {
36778             value += "";
36779             r = new RegExp(",", "g");
36780             value = value.replace(r, "");
36781         }
36782         
36783         var nan = isNaN(value);
36784         
36785         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36786             return nan ? '' : value;
36787         }
36788         return parseFloat(value).toFixed(this.decimalPrecision);
36789     },
36790
36791     setValue : function(v)
36792     {
36793         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36794         
36795         this.value = v;
36796         
36797         if(this.rendered){
36798             
36799             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36800             
36801             this.inputEl().dom.value = (v == '') ? '' :
36802                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36803             
36804             if(!this.allowZero && v === '0') {
36805                 this.hiddenEl().dom.value = '';
36806                 this.inputEl().dom.value = '';
36807             }
36808             
36809             this.validate();
36810         }
36811     },
36812
36813     decimalPrecisionFcn : function(v)
36814     {
36815         return Math.floor(v);
36816     },
36817
36818     beforeBlur : function()
36819     {
36820         var v = this.parseValue(this.getRawValue());
36821         
36822         if(v || v === 0 || v === ''){
36823             this.setValue(v);
36824         }
36825     },
36826     
36827     hiddenEl : function()
36828     {
36829         return this.el.select('input.hidden-number-input',true).first();
36830     }
36831     
36832 });
36833
36834  
36835
36836 /*
36837 * Licence: LGPL
36838 */
36839
36840 /**
36841  * @class Roo.bootstrap.DocumentSlider
36842  * @extends Roo.bootstrap.Component
36843  * Bootstrap DocumentSlider class
36844  * 
36845  * @constructor
36846  * Create a new DocumentViewer
36847  * @param {Object} config The config object
36848  */
36849
36850 Roo.bootstrap.DocumentSlider = function(config){
36851     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36852     
36853     this.files = [];
36854     
36855     this.addEvents({
36856         /**
36857          * @event initial
36858          * Fire after initEvent
36859          * @param {Roo.bootstrap.DocumentSlider} this
36860          */
36861         "initial" : true,
36862         /**
36863          * @event update
36864          * Fire after update
36865          * @param {Roo.bootstrap.DocumentSlider} this
36866          */
36867         "update" : true,
36868         /**
36869          * @event click
36870          * Fire after click
36871          * @param {Roo.bootstrap.DocumentSlider} this
36872          */
36873         "click" : true
36874     });
36875 };
36876
36877 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36878     
36879     files : false,
36880     
36881     indicator : 0,
36882     
36883     getAutoCreate : function()
36884     {
36885         var cfg = {
36886             tag : 'div',
36887             cls : 'roo-document-slider',
36888             cn : [
36889                 {
36890                     tag : 'div',
36891                     cls : 'roo-document-slider-header',
36892                     cn : [
36893                         {
36894                             tag : 'div',
36895                             cls : 'roo-document-slider-header-title'
36896                         }
36897                     ]
36898                 },
36899                 {
36900                     tag : 'div',
36901                     cls : 'roo-document-slider-body',
36902                     cn : [
36903                         {
36904                             tag : 'div',
36905                             cls : 'roo-document-slider-prev',
36906                             cn : [
36907                                 {
36908                                     tag : 'i',
36909                                     cls : 'fa fa-chevron-left'
36910                                 }
36911                             ]
36912                         },
36913                         {
36914                             tag : 'div',
36915                             cls : 'roo-document-slider-thumb',
36916                             cn : [
36917                                 {
36918                                     tag : 'img',
36919                                     cls : 'roo-document-slider-image'
36920                                 }
36921                             ]
36922                         },
36923                         {
36924                             tag : 'div',
36925                             cls : 'roo-document-slider-next',
36926                             cn : [
36927                                 {
36928                                     tag : 'i',
36929                                     cls : 'fa fa-chevron-right'
36930                                 }
36931                             ]
36932                         }
36933                     ]
36934                 }
36935             ]
36936         };
36937         
36938         return cfg;
36939     },
36940     
36941     initEvents : function()
36942     {
36943         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36944         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36945         
36946         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36947         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36948         
36949         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36950         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36951         
36952         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36953         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36954         
36955         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36956         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36957         
36958         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36959         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36960         
36961         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36962         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36963         
36964         this.thumbEl.on('click', this.onClick, this);
36965         
36966         this.prevIndicator.on('click', this.prev, this);
36967         
36968         this.nextIndicator.on('click', this.next, this);
36969         
36970     },
36971     
36972     initial : function()
36973     {
36974         if(this.files.length){
36975             this.indicator = 1;
36976             this.update()
36977         }
36978         
36979         this.fireEvent('initial', this);
36980     },
36981     
36982     update : function()
36983     {
36984         this.imageEl.attr('src', this.files[this.indicator - 1]);
36985         
36986         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36987         
36988         this.prevIndicator.show();
36989         
36990         if(this.indicator == 1){
36991             this.prevIndicator.hide();
36992         }
36993         
36994         this.nextIndicator.show();
36995         
36996         if(this.indicator == this.files.length){
36997             this.nextIndicator.hide();
36998         }
36999         
37000         this.thumbEl.scrollTo('top');
37001         
37002         this.fireEvent('update', this);
37003     },
37004     
37005     onClick : function(e)
37006     {
37007         e.preventDefault();
37008         
37009         this.fireEvent('click', this);
37010     },
37011     
37012     prev : function(e)
37013     {
37014         e.preventDefault();
37015         
37016         this.indicator = Math.max(1, this.indicator - 1);
37017         
37018         this.update();
37019     },
37020     
37021     next : function(e)
37022     {
37023         e.preventDefault();
37024         
37025         this.indicator = Math.min(this.files.length, this.indicator + 1);
37026         
37027         this.update();
37028     }
37029 });
37030 /*
37031  * - LGPL
37032  *
37033  * RadioSet
37034  *
37035  *
37036  */
37037
37038 /**
37039  * @class Roo.bootstrap.RadioSet
37040  * @extends Roo.bootstrap.Input
37041  * @children Roo.bootstrap.Radio
37042  * Bootstrap RadioSet class
37043  * @cfg {String} indicatorpos (left|right) default left
37044  * @cfg {Boolean} inline (true|false) inline the element (default true)
37045  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37046  * @constructor
37047  * Create a new RadioSet
37048  * @param {Object} config The config object
37049  */
37050
37051 Roo.bootstrap.RadioSet = function(config){
37052     
37053     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37054     
37055     this.radioes = [];
37056     
37057     Roo.bootstrap.RadioSet.register(this);
37058     
37059     this.addEvents({
37060         /**
37061         * @event check
37062         * Fires when the element is checked or unchecked.
37063         * @param {Roo.bootstrap.RadioSet} this This radio
37064         * @param {Roo.bootstrap.Radio} item The checked item
37065         */
37066        check : true,
37067        /**
37068         * @event click
37069         * Fires when the element is click.
37070         * @param {Roo.bootstrap.RadioSet} this This radio set
37071         * @param {Roo.bootstrap.Radio} item The checked item
37072         * @param {Roo.EventObject} e The event object
37073         */
37074        click : true
37075     });
37076     
37077 };
37078
37079 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37080
37081     radioes : false,
37082     
37083     inline : true,
37084     
37085     weight : '',
37086     
37087     indicatorpos : 'left',
37088     
37089     getAutoCreate : function()
37090     {
37091         var label = {
37092             tag : 'label',
37093             cls : 'roo-radio-set-label',
37094             cn : [
37095                 {
37096                     tag : 'span',
37097                     html : this.fieldLabel
37098                 }
37099             ]
37100         };
37101         if (Roo.bootstrap.version == 3) {
37102             
37103             
37104             if(this.indicatorpos == 'left'){
37105                 label.cn.unshift({
37106                     tag : 'i',
37107                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37108                     tooltip : 'This field is required'
37109                 });
37110             } else {
37111                 label.cn.push({
37112                     tag : 'i',
37113                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37114                     tooltip : 'This field is required'
37115                 });
37116             }
37117         }
37118         var items = {
37119             tag : 'div',
37120             cls : 'roo-radio-set-items'
37121         };
37122         
37123         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37124         
37125         if (align === 'left' && this.fieldLabel.length) {
37126             
37127             items = {
37128                 cls : "roo-radio-set-right", 
37129                 cn: [
37130                     items
37131                 ]
37132             };
37133             
37134             if(this.labelWidth > 12){
37135                 label.style = "width: " + this.labelWidth + 'px';
37136             }
37137             
37138             if(this.labelWidth < 13 && this.labelmd == 0){
37139                 this.labelmd = this.labelWidth;
37140             }
37141             
37142             if(this.labellg > 0){
37143                 label.cls += ' col-lg-' + this.labellg;
37144                 items.cls += ' col-lg-' + (12 - this.labellg);
37145             }
37146             
37147             if(this.labelmd > 0){
37148                 label.cls += ' col-md-' + this.labelmd;
37149                 items.cls += ' col-md-' + (12 - this.labelmd);
37150             }
37151             
37152             if(this.labelsm > 0){
37153                 label.cls += ' col-sm-' + this.labelsm;
37154                 items.cls += ' col-sm-' + (12 - this.labelsm);
37155             }
37156             
37157             if(this.labelxs > 0){
37158                 label.cls += ' col-xs-' + this.labelxs;
37159                 items.cls += ' col-xs-' + (12 - this.labelxs);
37160             }
37161         }
37162         
37163         var cfg = {
37164             tag : 'div',
37165             cls : 'roo-radio-set',
37166             cn : [
37167                 {
37168                     tag : 'input',
37169                     cls : 'roo-radio-set-input',
37170                     type : 'hidden',
37171                     name : this.name,
37172                     value : this.value ? this.value :  ''
37173                 },
37174                 label,
37175                 items
37176             ]
37177         };
37178         
37179         if(this.weight.length){
37180             cfg.cls += ' roo-radio-' + this.weight;
37181         }
37182         
37183         if(this.inline) {
37184             cfg.cls += ' roo-radio-set-inline';
37185         }
37186         
37187         var settings=this;
37188         ['xs','sm','md','lg'].map(function(size){
37189             if (settings[size]) {
37190                 cfg.cls += ' col-' + size + '-' + settings[size];
37191             }
37192         });
37193         
37194         return cfg;
37195         
37196     },
37197
37198     initEvents : function()
37199     {
37200         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37201         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37202         
37203         if(!this.fieldLabel.length){
37204             this.labelEl.hide();
37205         }
37206         
37207         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37208         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37209         
37210         this.indicator = this.indicatorEl();
37211         
37212         if(this.indicator){
37213             this.indicator.addClass('invisible');
37214         }
37215         
37216         this.originalValue = this.getValue();
37217         
37218     },
37219     
37220     inputEl: function ()
37221     {
37222         return this.el.select('.roo-radio-set-input', true).first();
37223     },
37224     
37225     getChildContainer : function()
37226     {
37227         return this.itemsEl;
37228     },
37229     
37230     register : function(item)
37231     {
37232         this.radioes.push(item);
37233         
37234     },
37235     
37236     validate : function()
37237     {   
37238         if(this.getVisibilityEl().hasClass('hidden')){
37239             return true;
37240         }
37241         
37242         var valid = false;
37243         
37244         Roo.each(this.radioes, function(i){
37245             if(!i.checked){
37246                 return;
37247             }
37248             
37249             valid = true;
37250             return false;
37251         });
37252         
37253         if(this.allowBlank) {
37254             return true;
37255         }
37256         
37257         if(this.disabled || valid){
37258             this.markValid();
37259             return true;
37260         }
37261         
37262         this.markInvalid();
37263         return false;
37264         
37265     },
37266     
37267     markValid : function()
37268     {
37269         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37270             this.indicatorEl().removeClass('visible');
37271             this.indicatorEl().addClass('invisible');
37272         }
37273         
37274         
37275         if (Roo.bootstrap.version == 3) {
37276             this.el.removeClass([this.invalidClass, this.validClass]);
37277             this.el.addClass(this.validClass);
37278         } else {
37279             this.el.removeClass(['is-invalid','is-valid']);
37280             this.el.addClass(['is-valid']);
37281         }
37282         this.fireEvent('valid', this);
37283     },
37284     
37285     markInvalid : function(msg)
37286     {
37287         if(this.allowBlank || this.disabled){
37288             return;
37289         }
37290         
37291         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37292             this.indicatorEl().removeClass('invisible');
37293             this.indicatorEl().addClass('visible');
37294         }
37295         if (Roo.bootstrap.version == 3) {
37296             this.el.removeClass([this.invalidClass, this.validClass]);
37297             this.el.addClass(this.invalidClass);
37298         } else {
37299             this.el.removeClass(['is-invalid','is-valid']);
37300             this.el.addClass(['is-invalid']);
37301         }
37302         
37303         this.fireEvent('invalid', this, msg);
37304         
37305     },
37306     
37307     setValue : function(v, suppressEvent)
37308     {   
37309         if(this.value === v){
37310             return;
37311         }
37312         
37313         this.value = v;
37314         
37315         if(this.rendered){
37316             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37317         }
37318         
37319         Roo.each(this.radioes, function(i){
37320             i.checked = false;
37321             i.el.removeClass('checked');
37322         });
37323         
37324         Roo.each(this.radioes, function(i){
37325             
37326             if(i.value === v || i.value.toString() === v.toString()){
37327                 i.checked = true;
37328                 i.el.addClass('checked');
37329                 
37330                 if(suppressEvent !== true){
37331                     this.fireEvent('check', this, i);
37332                 }
37333                 
37334                 return false;
37335             }
37336             
37337         }, this);
37338         
37339         this.validate();
37340     },
37341     
37342     clearInvalid : function(){
37343         
37344         if(!this.el || this.preventMark){
37345             return;
37346         }
37347         
37348         this.el.removeClass([this.invalidClass]);
37349         
37350         this.fireEvent('valid', this);
37351     }
37352     
37353 });
37354
37355 Roo.apply(Roo.bootstrap.RadioSet, {
37356     
37357     groups: {},
37358     
37359     register : function(set)
37360     {
37361         this.groups[set.name] = set;
37362     },
37363     
37364     get: function(name) 
37365     {
37366         if (typeof(this.groups[name]) == 'undefined') {
37367             return false;
37368         }
37369         
37370         return this.groups[name] ;
37371     }
37372     
37373 });
37374 /*
37375  * Based on:
37376  * Ext JS Library 1.1.1
37377  * Copyright(c) 2006-2007, Ext JS, LLC.
37378  *
37379  * Originally Released Under LGPL - original licence link has changed is not relivant.
37380  *
37381  * Fork - LGPL
37382  * <script type="text/javascript">
37383  */
37384
37385
37386 /**
37387  * @class Roo.bootstrap.SplitBar
37388  * @extends Roo.util.Observable
37389  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37390  * <br><br>
37391  * Usage:
37392  * <pre><code>
37393 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37394                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37395 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37396 split.minSize = 100;
37397 split.maxSize = 600;
37398 split.animate = true;
37399 split.on('moved', splitterMoved);
37400 </code></pre>
37401  * @constructor
37402  * Create a new SplitBar
37403  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37404  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37405  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37406  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37407                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37408                         position of the SplitBar).
37409  */
37410 Roo.bootstrap.SplitBar = function(cfg){
37411     
37412     /** @private */
37413     
37414     //{
37415     //  dragElement : elm
37416     //  resizingElement: el,
37417         // optional..
37418     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37419     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37420         // existingProxy ???
37421     //}
37422     
37423     this.el = Roo.get(cfg.dragElement, true);
37424     this.el.dom.unselectable = "on";
37425     /** @private */
37426     this.resizingEl = Roo.get(cfg.resizingElement, true);
37427
37428     /**
37429      * @private
37430      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37431      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37432      * @type Number
37433      */
37434     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37435     
37436     /**
37437      * The minimum size of the resizing element. (Defaults to 0)
37438      * @type Number
37439      */
37440     this.minSize = 0;
37441     
37442     /**
37443      * The maximum size of the resizing element. (Defaults to 2000)
37444      * @type Number
37445      */
37446     this.maxSize = 2000;
37447     
37448     /**
37449      * Whether to animate the transition to the new size
37450      * @type Boolean
37451      */
37452     this.animate = false;
37453     
37454     /**
37455      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37456      * @type Boolean
37457      */
37458     this.useShim = false;
37459     
37460     /** @private */
37461     this.shim = null;
37462     
37463     if(!cfg.existingProxy){
37464         /** @private */
37465         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37466     }else{
37467         this.proxy = Roo.get(cfg.existingProxy).dom;
37468     }
37469     /** @private */
37470     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37471     
37472     /** @private */
37473     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37474     
37475     /** @private */
37476     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37477     
37478     /** @private */
37479     this.dragSpecs = {};
37480     
37481     /**
37482      * @private The adapter to use to positon and resize elements
37483      */
37484     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37485     this.adapter.init(this);
37486     
37487     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37488         /** @private */
37489         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37490         this.el.addClass("roo-splitbar-h");
37491     }else{
37492         /** @private */
37493         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37494         this.el.addClass("roo-splitbar-v");
37495     }
37496     
37497     this.addEvents({
37498         /**
37499          * @event resize
37500          * Fires when the splitter is moved (alias for {@link #event-moved})
37501          * @param {Roo.bootstrap.SplitBar} this
37502          * @param {Number} newSize the new width or height
37503          */
37504         "resize" : true,
37505         /**
37506          * @event moved
37507          * Fires when the splitter is moved
37508          * @param {Roo.bootstrap.SplitBar} this
37509          * @param {Number} newSize the new width or height
37510          */
37511         "moved" : true,
37512         /**
37513          * @event beforeresize
37514          * Fires before the splitter is dragged
37515          * @param {Roo.bootstrap.SplitBar} this
37516          */
37517         "beforeresize" : true,
37518
37519         "beforeapply" : true
37520     });
37521
37522     Roo.util.Observable.call(this);
37523 };
37524
37525 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37526     onStartProxyDrag : function(x, y){
37527         this.fireEvent("beforeresize", this);
37528         if(!this.overlay){
37529             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37530             o.unselectable();
37531             o.enableDisplayMode("block");
37532             // all splitbars share the same overlay
37533             Roo.bootstrap.SplitBar.prototype.overlay = o;
37534         }
37535         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37536         this.overlay.show();
37537         Roo.get(this.proxy).setDisplayed("block");
37538         var size = this.adapter.getElementSize(this);
37539         this.activeMinSize = this.getMinimumSize();;
37540         this.activeMaxSize = this.getMaximumSize();;
37541         var c1 = size - this.activeMinSize;
37542         var c2 = Math.max(this.activeMaxSize - size, 0);
37543         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37544             this.dd.resetConstraints();
37545             this.dd.setXConstraint(
37546                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37547                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37548             );
37549             this.dd.setYConstraint(0, 0);
37550         }else{
37551             this.dd.resetConstraints();
37552             this.dd.setXConstraint(0, 0);
37553             this.dd.setYConstraint(
37554                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37555                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37556             );
37557          }
37558         this.dragSpecs.startSize = size;
37559         this.dragSpecs.startPoint = [x, y];
37560         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37561     },
37562     
37563     /** 
37564      * @private Called after the drag operation by the DDProxy
37565      */
37566     onEndProxyDrag : function(e){
37567         Roo.get(this.proxy).setDisplayed(false);
37568         var endPoint = Roo.lib.Event.getXY(e);
37569         if(this.overlay){
37570             this.overlay.hide();
37571         }
37572         var newSize;
37573         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37574             newSize = this.dragSpecs.startSize + 
37575                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37576                     endPoint[0] - this.dragSpecs.startPoint[0] :
37577                     this.dragSpecs.startPoint[0] - endPoint[0]
37578                 );
37579         }else{
37580             newSize = this.dragSpecs.startSize + 
37581                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37582                     endPoint[1] - this.dragSpecs.startPoint[1] :
37583                     this.dragSpecs.startPoint[1] - endPoint[1]
37584                 );
37585         }
37586         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37587         if(newSize != this.dragSpecs.startSize){
37588             if(this.fireEvent('beforeapply', this, newSize) !== false){
37589                 this.adapter.setElementSize(this, newSize);
37590                 this.fireEvent("moved", this, newSize);
37591                 this.fireEvent("resize", this, newSize);
37592             }
37593         }
37594     },
37595     
37596     /**
37597      * Get the adapter this SplitBar uses
37598      * @return The adapter object
37599      */
37600     getAdapter : function(){
37601         return this.adapter;
37602     },
37603     
37604     /**
37605      * Set the adapter this SplitBar uses
37606      * @param {Object} adapter A SplitBar adapter object
37607      */
37608     setAdapter : function(adapter){
37609         this.adapter = adapter;
37610         this.adapter.init(this);
37611     },
37612     
37613     /**
37614      * Gets the minimum size for the resizing element
37615      * @return {Number} The minimum size
37616      */
37617     getMinimumSize : function(){
37618         return this.minSize;
37619     },
37620     
37621     /**
37622      * Sets the minimum size for the resizing element
37623      * @param {Number} minSize The minimum size
37624      */
37625     setMinimumSize : function(minSize){
37626         this.minSize = minSize;
37627     },
37628     
37629     /**
37630      * Gets the maximum size for the resizing element
37631      * @return {Number} The maximum size
37632      */
37633     getMaximumSize : function(){
37634         return this.maxSize;
37635     },
37636     
37637     /**
37638      * Sets the maximum size for the resizing element
37639      * @param {Number} maxSize The maximum size
37640      */
37641     setMaximumSize : function(maxSize){
37642         this.maxSize = maxSize;
37643     },
37644     
37645     /**
37646      * Sets the initialize size for the resizing element
37647      * @param {Number} size The initial size
37648      */
37649     setCurrentSize : function(size){
37650         var oldAnimate = this.animate;
37651         this.animate = false;
37652         this.adapter.setElementSize(this, size);
37653         this.animate = oldAnimate;
37654     },
37655     
37656     /**
37657      * Destroy this splitbar. 
37658      * @param {Boolean} removeEl True to remove the element
37659      */
37660     destroy : function(removeEl){
37661         if(this.shim){
37662             this.shim.remove();
37663         }
37664         this.dd.unreg();
37665         this.proxy.parentNode.removeChild(this.proxy);
37666         if(removeEl){
37667             this.el.remove();
37668         }
37669     }
37670 });
37671
37672 /**
37673  * @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.
37674  */
37675 Roo.bootstrap.SplitBar.createProxy = function(dir){
37676     var proxy = new Roo.Element(document.createElement("div"));
37677     proxy.unselectable();
37678     var cls = 'roo-splitbar-proxy';
37679     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37680     document.body.appendChild(proxy.dom);
37681     return proxy.dom;
37682 };
37683
37684 /** 
37685  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37686  * Default Adapter. It assumes the splitter and resizing element are not positioned
37687  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37688  */
37689 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37690 };
37691
37692 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37693     // do nothing for now
37694     init : function(s){
37695     
37696     },
37697     /**
37698      * Called before drag operations to get the current size of the resizing element. 
37699      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37700      */
37701      getElementSize : function(s){
37702         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37703             return s.resizingEl.getWidth();
37704         }else{
37705             return s.resizingEl.getHeight();
37706         }
37707     },
37708     
37709     /**
37710      * Called after drag operations to set the size of the resizing element.
37711      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37712      * @param {Number} newSize The new size to set
37713      * @param {Function} onComplete A function to be invoked when resizing is complete
37714      */
37715     setElementSize : function(s, newSize, onComplete){
37716         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37717             if(!s.animate){
37718                 s.resizingEl.setWidth(newSize);
37719                 if(onComplete){
37720                     onComplete(s, newSize);
37721                 }
37722             }else{
37723                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37724             }
37725         }else{
37726             
37727             if(!s.animate){
37728                 s.resizingEl.setHeight(newSize);
37729                 if(onComplete){
37730                     onComplete(s, newSize);
37731                 }
37732             }else{
37733                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37734             }
37735         }
37736     }
37737 };
37738
37739 /** 
37740  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37741  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37742  * Adapter that  moves the splitter element to align with the resized sizing element. 
37743  * Used with an absolute positioned SplitBar.
37744  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37745  * document.body, make sure you assign an id to the body element.
37746  */
37747 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37748     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37749     this.container = Roo.get(container);
37750 };
37751
37752 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37753     init : function(s){
37754         this.basic.init(s);
37755     },
37756     
37757     getElementSize : function(s){
37758         return this.basic.getElementSize(s);
37759     },
37760     
37761     setElementSize : function(s, newSize, onComplete){
37762         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37763     },
37764     
37765     moveSplitter : function(s){
37766         var yes = Roo.bootstrap.SplitBar;
37767         switch(s.placement){
37768             case yes.LEFT:
37769                 s.el.setX(s.resizingEl.getRight());
37770                 break;
37771             case yes.RIGHT:
37772                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37773                 break;
37774             case yes.TOP:
37775                 s.el.setY(s.resizingEl.getBottom());
37776                 break;
37777             case yes.BOTTOM:
37778                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37779                 break;
37780         }
37781     }
37782 };
37783
37784 /**
37785  * Orientation constant - Create a vertical SplitBar
37786  * @static
37787  * @type Number
37788  */
37789 Roo.bootstrap.SplitBar.VERTICAL = 1;
37790
37791 /**
37792  * Orientation constant - Create a horizontal SplitBar
37793  * @static
37794  * @type Number
37795  */
37796 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37797
37798 /**
37799  * Placement constant - The resizing element is to the left of the splitter element
37800  * @static
37801  * @type Number
37802  */
37803 Roo.bootstrap.SplitBar.LEFT = 1;
37804
37805 /**
37806  * Placement constant - The resizing element is to the right of the splitter element
37807  * @static
37808  * @type Number
37809  */
37810 Roo.bootstrap.SplitBar.RIGHT = 2;
37811
37812 /**
37813  * Placement constant - The resizing element is positioned above the splitter element
37814  * @static
37815  * @type Number
37816  */
37817 Roo.bootstrap.SplitBar.TOP = 3;
37818
37819 /**
37820  * Placement constant - The resizing element is positioned under splitter element
37821  * @static
37822  * @type Number
37823  */
37824 Roo.bootstrap.SplitBar.BOTTOM = 4;
37825 Roo.namespace("Roo.bootstrap.layout");/*
37826  * Based on:
37827  * Ext JS Library 1.1.1
37828  * Copyright(c) 2006-2007, Ext JS, LLC.
37829  *
37830  * Originally Released Under LGPL - original licence link has changed is not relivant.
37831  *
37832  * Fork - LGPL
37833  * <script type="text/javascript">
37834  */
37835
37836 /**
37837  * @class Roo.bootstrap.layout.Manager
37838  * @extends Roo.bootstrap.Component
37839  * Base class for layout managers.
37840  */
37841 Roo.bootstrap.layout.Manager = function(config)
37842 {
37843     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37844
37845
37846
37847
37848
37849     /** false to disable window resize monitoring @type Boolean */
37850     this.monitorWindowResize = true;
37851     this.regions = {};
37852     this.addEvents({
37853         /**
37854          * @event layout
37855          * Fires when a layout is performed.
37856          * @param {Roo.LayoutManager} this
37857          */
37858         "layout" : true,
37859         /**
37860          * @event regionresized
37861          * Fires when the user resizes a region.
37862          * @param {Roo.LayoutRegion} region The resized region
37863          * @param {Number} newSize The new size (width for east/west, height for north/south)
37864          */
37865         "regionresized" : true,
37866         /**
37867          * @event regioncollapsed
37868          * Fires when a region is collapsed.
37869          * @param {Roo.LayoutRegion} region The collapsed region
37870          */
37871         "regioncollapsed" : true,
37872         /**
37873          * @event regionexpanded
37874          * Fires when a region is expanded.
37875          * @param {Roo.LayoutRegion} region The expanded region
37876          */
37877         "regionexpanded" : true
37878     });
37879     this.updating = false;
37880
37881     if (config.el) {
37882         this.el = Roo.get(config.el);
37883         this.initEvents();
37884     }
37885
37886 };
37887
37888 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37889
37890
37891     regions : null,
37892
37893     monitorWindowResize : true,
37894
37895
37896     updating : false,
37897
37898
37899     onRender : function(ct, position)
37900     {
37901         if(!this.el){
37902             this.el = Roo.get(ct);
37903             this.initEvents();
37904         }
37905         //this.fireEvent('render',this);
37906     },
37907
37908
37909     initEvents: function()
37910     {
37911
37912
37913         // ie scrollbar fix
37914         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37915             document.body.scroll = "no";
37916         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37917             this.el.position('relative');
37918         }
37919         this.id = this.el.id;
37920         this.el.addClass("roo-layout-container");
37921         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37922         if(this.el.dom != document.body ) {
37923             this.el.on('resize', this.layout,this);
37924             this.el.on('show', this.layout,this);
37925         }
37926
37927     },
37928
37929     /**
37930      * Returns true if this layout is currently being updated
37931      * @return {Boolean}
37932      */
37933     isUpdating : function(){
37934         return this.updating;
37935     },
37936
37937     /**
37938      * Suspend the LayoutManager from doing auto-layouts while
37939      * making multiple add or remove calls
37940      */
37941     beginUpdate : function(){
37942         this.updating = true;
37943     },
37944
37945     /**
37946      * Restore auto-layouts and optionally disable the manager from performing a layout
37947      * @param {Boolean} noLayout true to disable a layout update
37948      */
37949     endUpdate : function(noLayout){
37950         this.updating = false;
37951         if(!noLayout){
37952             this.layout();
37953         }
37954     },
37955
37956     layout: function(){
37957         // abstract...
37958     },
37959
37960     onRegionResized : function(region, newSize){
37961         this.fireEvent("regionresized", region, newSize);
37962         this.layout();
37963     },
37964
37965     onRegionCollapsed : function(region){
37966         this.fireEvent("regioncollapsed", region);
37967     },
37968
37969     onRegionExpanded : function(region){
37970         this.fireEvent("regionexpanded", region);
37971     },
37972
37973     /**
37974      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37975      * performs box-model adjustments.
37976      * @return {Object} The size as an object {width: (the width), height: (the height)}
37977      */
37978     getViewSize : function()
37979     {
37980         var size;
37981         if(this.el.dom != document.body){
37982             size = this.el.getSize();
37983         }else{
37984             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37985         }
37986         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37987         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37988         return size;
37989     },
37990
37991     /**
37992      * Returns the Element this layout is bound to.
37993      * @return {Roo.Element}
37994      */
37995     getEl : function(){
37996         return this.el;
37997     },
37998
37999     /**
38000      * Returns the specified region.
38001      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38002      * @return {Roo.LayoutRegion}
38003      */
38004     getRegion : function(target){
38005         return this.regions[target.toLowerCase()];
38006     },
38007
38008     onWindowResize : function(){
38009         if(this.monitorWindowResize){
38010             this.layout();
38011         }
38012     }
38013 });
38014 /*
38015  * Based on:
38016  * Ext JS Library 1.1.1
38017  * Copyright(c) 2006-2007, Ext JS, LLC.
38018  *
38019  * Originally Released Under LGPL - original licence link has changed is not relivant.
38020  *
38021  * Fork - LGPL
38022  * <script type="text/javascript">
38023  */
38024 /**
38025  * @class Roo.bootstrap.layout.Border
38026  * @extends Roo.bootstrap.layout.Manager
38027  * @builder-top
38028  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38029  * please see: examples/bootstrap/nested.html<br><br>
38030  
38031 <b>The container the layout is rendered into can be either the body element or any other element.
38032 If it is not the body element, the container needs to either be an absolute positioned element,
38033 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38034 the container size if it is not the body element.</b>
38035
38036 * @constructor
38037 * Create a new Border
38038 * @param {Object} config Configuration options
38039  */
38040 Roo.bootstrap.layout.Border = function(config){
38041     config = config || {};
38042     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38043     
38044     
38045     
38046     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38047         if(config[region]){
38048             config[region].region = region;
38049             this.addRegion(config[region]);
38050         }
38051     },this);
38052     
38053 };
38054
38055 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38056
38057 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38058     
38059     parent : false, // this might point to a 'nest' or a ???
38060     
38061     /**
38062      * Creates and adds a new region if it doesn't already exist.
38063      * @param {String} target The target region key (north, south, east, west or center).
38064      * @param {Object} config The regions config object
38065      * @return {BorderLayoutRegion} The new region
38066      */
38067     addRegion : function(config)
38068     {
38069         if(!this.regions[config.region]){
38070             var r = this.factory(config);
38071             this.bindRegion(r);
38072         }
38073         return this.regions[config.region];
38074     },
38075
38076     // private (kinda)
38077     bindRegion : function(r){
38078         this.regions[r.config.region] = r;
38079         
38080         r.on("visibilitychange",    this.layout, this);
38081         r.on("paneladded",          this.layout, this);
38082         r.on("panelremoved",        this.layout, this);
38083         r.on("invalidated",         this.layout, this);
38084         r.on("resized",             this.onRegionResized, this);
38085         r.on("collapsed",           this.onRegionCollapsed, this);
38086         r.on("expanded",            this.onRegionExpanded, this);
38087     },
38088
38089     /**
38090      * Performs a layout update.
38091      */
38092     layout : function()
38093     {
38094         if(this.updating) {
38095             return;
38096         }
38097         
38098         // render all the rebions if they have not been done alreayd?
38099         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38100             if(this.regions[region] && !this.regions[region].bodyEl){
38101                 this.regions[region].onRender(this.el)
38102             }
38103         },this);
38104         
38105         var size = this.getViewSize();
38106         var w = size.width;
38107         var h = size.height;
38108         var centerW = w;
38109         var centerH = h;
38110         var centerY = 0;
38111         var centerX = 0;
38112         //var x = 0, y = 0;
38113
38114         var rs = this.regions;
38115         var north = rs["north"];
38116         var south = rs["south"]; 
38117         var west = rs["west"];
38118         var east = rs["east"];
38119         var center = rs["center"];
38120         //if(this.hideOnLayout){ // not supported anymore
38121             //c.el.setStyle("display", "none");
38122         //}
38123         if(north && north.isVisible()){
38124             var b = north.getBox();
38125             var m = north.getMargins();
38126             b.width = w - (m.left+m.right);
38127             b.x = m.left;
38128             b.y = m.top;
38129             centerY = b.height + b.y + m.bottom;
38130             centerH -= centerY;
38131             north.updateBox(this.safeBox(b));
38132         }
38133         if(south && south.isVisible()){
38134             var b = south.getBox();
38135             var m = south.getMargins();
38136             b.width = w - (m.left+m.right);
38137             b.x = m.left;
38138             var totalHeight = (b.height + m.top + m.bottom);
38139             b.y = h - totalHeight + m.top;
38140             centerH -= totalHeight;
38141             south.updateBox(this.safeBox(b));
38142         }
38143         if(west && west.isVisible()){
38144             var b = west.getBox();
38145             var m = west.getMargins();
38146             b.height = centerH - (m.top+m.bottom);
38147             b.x = m.left;
38148             b.y = centerY + m.top;
38149             var totalWidth = (b.width + m.left + m.right);
38150             centerX += totalWidth;
38151             centerW -= totalWidth;
38152             west.updateBox(this.safeBox(b));
38153         }
38154         if(east && east.isVisible()){
38155             var b = east.getBox();
38156             var m = east.getMargins();
38157             b.height = centerH - (m.top+m.bottom);
38158             var totalWidth = (b.width + m.left + m.right);
38159             b.x = w - totalWidth + m.left;
38160             b.y = centerY + m.top;
38161             centerW -= totalWidth;
38162             east.updateBox(this.safeBox(b));
38163         }
38164         if(center){
38165             var m = center.getMargins();
38166             var centerBox = {
38167                 x: centerX + m.left,
38168                 y: centerY + m.top,
38169                 width: centerW - (m.left+m.right),
38170                 height: centerH - (m.top+m.bottom)
38171             };
38172             //if(this.hideOnLayout){
38173                 //center.el.setStyle("display", "block");
38174             //}
38175             center.updateBox(this.safeBox(centerBox));
38176         }
38177         this.el.repaint();
38178         this.fireEvent("layout", this);
38179     },
38180
38181     // private
38182     safeBox : function(box){
38183         box.width = Math.max(0, box.width);
38184         box.height = Math.max(0, box.height);
38185         return box;
38186     },
38187
38188     /**
38189      * Adds a ContentPanel (or subclass) to this layout.
38190      * @param {String} target The target region key (north, south, east, west or center).
38191      * @param {Roo.ContentPanel} panel The panel to add
38192      * @return {Roo.ContentPanel} The added panel
38193      */
38194     add : function(target, panel){
38195          
38196         target = target.toLowerCase();
38197         return this.regions[target].add(panel);
38198     },
38199
38200     /**
38201      * Remove a ContentPanel (or subclass) to this layout.
38202      * @param {String} target The target region key (north, south, east, west or center).
38203      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38204      * @return {Roo.ContentPanel} The removed panel
38205      */
38206     remove : function(target, panel){
38207         target = target.toLowerCase();
38208         return this.regions[target].remove(panel);
38209     },
38210
38211     /**
38212      * Searches all regions for a panel with the specified id
38213      * @param {String} panelId
38214      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38215      */
38216     findPanel : function(panelId){
38217         var rs = this.regions;
38218         for(var target in rs){
38219             if(typeof rs[target] != "function"){
38220                 var p = rs[target].getPanel(panelId);
38221                 if(p){
38222                     return p;
38223                 }
38224             }
38225         }
38226         return null;
38227     },
38228
38229     /**
38230      * Searches all regions for a panel with the specified id and activates (shows) it.
38231      * @param {String/ContentPanel} panelId The panels id or the panel itself
38232      * @return {Roo.ContentPanel} The shown panel or null
38233      */
38234     showPanel : function(panelId) {
38235       var rs = this.regions;
38236       for(var target in rs){
38237          var r = rs[target];
38238          if(typeof r != "function"){
38239             if(r.hasPanel(panelId)){
38240                return r.showPanel(panelId);
38241             }
38242          }
38243       }
38244       return null;
38245    },
38246
38247    /**
38248      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38249      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38250      */
38251    /*
38252     restoreState : function(provider){
38253         if(!provider){
38254             provider = Roo.state.Manager;
38255         }
38256         var sm = new Roo.LayoutStateManager();
38257         sm.init(this, provider);
38258     },
38259 */
38260  
38261  
38262     /**
38263      * Adds a xtype elements to the layout.
38264      * <pre><code>
38265
38266 layout.addxtype({
38267        xtype : 'ContentPanel',
38268        region: 'west',
38269        items: [ .... ]
38270    }
38271 );
38272
38273 layout.addxtype({
38274         xtype : 'NestedLayoutPanel',
38275         region: 'west',
38276         layout: {
38277            center: { },
38278            west: { }   
38279         },
38280         items : [ ... list of content panels or nested layout panels.. ]
38281    }
38282 );
38283 </code></pre>
38284      * @param {Object} cfg Xtype definition of item to add.
38285      */
38286     addxtype : function(cfg)
38287     {
38288         // basically accepts a pannel...
38289         // can accept a layout region..!?!?
38290         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38291         
38292         
38293         // theory?  children can only be panels??
38294         
38295         //if (!cfg.xtype.match(/Panel$/)) {
38296         //    return false;
38297         //}
38298         var ret = false;
38299         
38300         if (typeof(cfg.region) == 'undefined') {
38301             Roo.log("Failed to add Panel, region was not set");
38302             Roo.log(cfg);
38303             return false;
38304         }
38305         var region = cfg.region;
38306         delete cfg.region;
38307         
38308           
38309         var xitems = [];
38310         if (cfg.items) {
38311             xitems = cfg.items;
38312             delete cfg.items;
38313         }
38314         var nb = false;
38315         
38316         if ( region == 'center') {
38317             Roo.log("Center: " + cfg.title);
38318         }
38319         
38320         
38321         switch(cfg.xtype) 
38322         {
38323             case 'Content':  // ContentPanel (el, cfg)
38324             case 'Scroll':  // ContentPanel (el, cfg)
38325             case 'View': 
38326                 cfg.autoCreate = cfg.autoCreate || true;
38327                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38328                 //} else {
38329                 //    var el = this.el.createChild();
38330                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38331                 //}
38332                 
38333                 this.add(region, ret);
38334                 break;
38335             
38336             /*
38337             case 'TreePanel': // our new panel!
38338                 cfg.el = this.el.createChild();
38339                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38340                 this.add(region, ret);
38341                 break;
38342             */
38343             
38344             case 'Nest': 
38345                 // create a new Layout (which is  a Border Layout...
38346                 
38347                 var clayout = cfg.layout;
38348                 clayout.el  = this.el.createChild();
38349                 clayout.items   = clayout.items  || [];
38350                 
38351                 delete cfg.layout;
38352                 
38353                 // replace this exitems with the clayout ones..
38354                 xitems = clayout.items;
38355                  
38356                 // force background off if it's in center...
38357                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38358                     cfg.background = false;
38359                 }
38360                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38361                 
38362                 
38363                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38364                 //console.log('adding nested layout panel '  + cfg.toSource());
38365                 this.add(region, ret);
38366                 nb = {}; /// find first...
38367                 break;
38368             
38369             case 'Grid':
38370                 
38371                 // needs grid and region
38372                 
38373                 //var el = this.getRegion(region).el.createChild();
38374                 /*
38375                  *var el = this.el.createChild();
38376                 // create the grid first...
38377                 cfg.grid.container = el;
38378                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38379                 */
38380                 
38381                 if (region == 'center' && this.active ) {
38382                     cfg.background = false;
38383                 }
38384                 
38385                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38386                 
38387                 this.add(region, ret);
38388                 /*
38389                 if (cfg.background) {
38390                     // render grid on panel activation (if panel background)
38391                     ret.on('activate', function(gp) {
38392                         if (!gp.grid.rendered) {
38393                     //        gp.grid.render(el);
38394                         }
38395                     });
38396                 } else {
38397                   //  cfg.grid.render(el);
38398                 }
38399                 */
38400                 break;
38401            
38402            
38403             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38404                 // it was the old xcomponent building that caused this before.
38405                 // espeically if border is the top element in the tree.
38406                 ret = this;
38407                 break; 
38408                 
38409                     
38410                 
38411                 
38412                 
38413             default:
38414                 /*
38415                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38416                     
38417                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38418                     this.add(region, ret);
38419                 } else {
38420                 */
38421                     Roo.log(cfg);
38422                     throw "Can not add '" + cfg.xtype + "' to Border";
38423                     return null;
38424              
38425                                 
38426              
38427         }
38428         this.beginUpdate();
38429         // add children..
38430         var region = '';
38431         var abn = {};
38432         Roo.each(xitems, function(i)  {
38433             region = nb && i.region ? i.region : false;
38434             
38435             var add = ret.addxtype(i);
38436            
38437             if (region) {
38438                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38439                 if (!i.background) {
38440                     abn[region] = nb[region] ;
38441                 }
38442             }
38443             
38444         });
38445         this.endUpdate();
38446
38447         // make the last non-background panel active..
38448         //if (nb) { Roo.log(abn); }
38449         if (nb) {
38450             
38451             for(var r in abn) {
38452                 region = this.getRegion(r);
38453                 if (region) {
38454                     // tried using nb[r], but it does not work..
38455                      
38456                     region.showPanel(abn[r]);
38457                    
38458                 }
38459             }
38460         }
38461         return ret;
38462         
38463     },
38464     
38465     
38466 // private
38467     factory : function(cfg)
38468     {
38469         
38470         var validRegions = Roo.bootstrap.layout.Border.regions;
38471
38472         var target = cfg.region;
38473         cfg.mgr = this;
38474         
38475         var r = Roo.bootstrap.layout;
38476         Roo.log(target);
38477         switch(target){
38478             case "north":
38479                 return new r.North(cfg);
38480             case "south":
38481                 return new r.South(cfg);
38482             case "east":
38483                 return new r.East(cfg);
38484             case "west":
38485                 return new r.West(cfg);
38486             case "center":
38487                 return new r.Center(cfg);
38488         }
38489         throw 'Layout region "'+target+'" not supported.';
38490     }
38491     
38492     
38493 });
38494  /*
38495  * Based on:
38496  * Ext JS Library 1.1.1
38497  * Copyright(c) 2006-2007, Ext JS, LLC.
38498  *
38499  * Originally Released Under LGPL - original licence link has changed is not relivant.
38500  *
38501  * Fork - LGPL
38502  * <script type="text/javascript">
38503  */
38504  
38505 /**
38506  * @class Roo.bootstrap.layout.Basic
38507  * @extends Roo.util.Observable
38508  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38509  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38510  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38511  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38512  * @cfg {string}   region  the region that it inhabits..
38513  * @cfg {bool}   skipConfig skip config?
38514  * 
38515
38516  */
38517 Roo.bootstrap.layout.Basic = function(config){
38518     
38519     this.mgr = config.mgr;
38520     
38521     this.position = config.region;
38522     
38523     var skipConfig = config.skipConfig;
38524     
38525     this.events = {
38526         /**
38527          * @scope Roo.BasicLayoutRegion
38528          */
38529         
38530         /**
38531          * @event beforeremove
38532          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38533          * @param {Roo.LayoutRegion} this
38534          * @param {Roo.ContentPanel} panel The panel
38535          * @param {Object} e The cancel event object
38536          */
38537         "beforeremove" : true,
38538         /**
38539          * @event invalidated
38540          * Fires when the layout for this region is changed.
38541          * @param {Roo.LayoutRegion} this
38542          */
38543         "invalidated" : true,
38544         /**
38545          * @event visibilitychange
38546          * Fires when this region is shown or hidden 
38547          * @param {Roo.LayoutRegion} this
38548          * @param {Boolean} visibility true or false
38549          */
38550         "visibilitychange" : true,
38551         /**
38552          * @event paneladded
38553          * Fires when a panel is added. 
38554          * @param {Roo.LayoutRegion} this
38555          * @param {Roo.ContentPanel} panel The panel
38556          */
38557         "paneladded" : true,
38558         /**
38559          * @event panelremoved
38560          * Fires when a panel is removed. 
38561          * @param {Roo.LayoutRegion} this
38562          * @param {Roo.ContentPanel} panel The panel
38563          */
38564         "panelremoved" : true,
38565         /**
38566          * @event beforecollapse
38567          * Fires when this region before collapse.
38568          * @param {Roo.LayoutRegion} this
38569          */
38570         "beforecollapse" : true,
38571         /**
38572          * @event collapsed
38573          * Fires when this region is collapsed.
38574          * @param {Roo.LayoutRegion} this
38575          */
38576         "collapsed" : true,
38577         /**
38578          * @event expanded
38579          * Fires when this region is expanded.
38580          * @param {Roo.LayoutRegion} this
38581          */
38582         "expanded" : true,
38583         /**
38584          * @event slideshow
38585          * Fires when this region is slid into view.
38586          * @param {Roo.LayoutRegion} this
38587          */
38588         "slideshow" : true,
38589         /**
38590          * @event slidehide
38591          * Fires when this region slides out of view. 
38592          * @param {Roo.LayoutRegion} this
38593          */
38594         "slidehide" : true,
38595         /**
38596          * @event panelactivated
38597          * Fires when a panel is activated. 
38598          * @param {Roo.LayoutRegion} this
38599          * @param {Roo.ContentPanel} panel The activated panel
38600          */
38601         "panelactivated" : true,
38602         /**
38603          * @event resized
38604          * Fires when the user resizes this region. 
38605          * @param {Roo.LayoutRegion} this
38606          * @param {Number} newSize The new size (width for east/west, height for north/south)
38607          */
38608         "resized" : true
38609     };
38610     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38611     this.panels = new Roo.util.MixedCollection();
38612     this.panels.getKey = this.getPanelId.createDelegate(this);
38613     this.box = null;
38614     this.activePanel = null;
38615     // ensure listeners are added...
38616     
38617     if (config.listeners || config.events) {
38618         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38619             listeners : config.listeners || {},
38620             events : config.events || {}
38621         });
38622     }
38623     
38624     if(skipConfig !== true){
38625         this.applyConfig(config);
38626     }
38627 };
38628
38629 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38630 {
38631     getPanelId : function(p){
38632         return p.getId();
38633     },
38634     
38635     applyConfig : function(config){
38636         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38637         this.config = config;
38638         
38639     },
38640     
38641     /**
38642      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38643      * the width, for horizontal (north, south) the height.
38644      * @param {Number} newSize The new width or height
38645      */
38646     resizeTo : function(newSize){
38647         var el = this.el ? this.el :
38648                  (this.activePanel ? this.activePanel.getEl() : null);
38649         if(el){
38650             switch(this.position){
38651                 case "east":
38652                 case "west":
38653                     el.setWidth(newSize);
38654                     this.fireEvent("resized", this, newSize);
38655                 break;
38656                 case "north":
38657                 case "south":
38658                     el.setHeight(newSize);
38659                     this.fireEvent("resized", this, newSize);
38660                 break;                
38661             }
38662         }
38663     },
38664     
38665     getBox : function(){
38666         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38667     },
38668     
38669     getMargins : function(){
38670         return this.margins;
38671     },
38672     
38673     updateBox : function(box){
38674         this.box = box;
38675         var el = this.activePanel.getEl();
38676         el.dom.style.left = box.x + "px";
38677         el.dom.style.top = box.y + "px";
38678         this.activePanel.setSize(box.width, box.height);
38679     },
38680     
38681     /**
38682      * Returns the container element for this region.
38683      * @return {Roo.Element}
38684      */
38685     getEl : function(){
38686         return this.activePanel;
38687     },
38688     
38689     /**
38690      * Returns true if this region is currently visible.
38691      * @return {Boolean}
38692      */
38693     isVisible : function(){
38694         return this.activePanel ? true : false;
38695     },
38696     
38697     setActivePanel : function(panel){
38698         panel = this.getPanel(panel);
38699         if(this.activePanel && this.activePanel != panel){
38700             this.activePanel.setActiveState(false);
38701             this.activePanel.getEl().setLeftTop(-10000,-10000);
38702         }
38703         this.activePanel = panel;
38704         panel.setActiveState(true);
38705         if(this.box){
38706             panel.setSize(this.box.width, this.box.height);
38707         }
38708         this.fireEvent("panelactivated", this, panel);
38709         this.fireEvent("invalidated");
38710     },
38711     
38712     /**
38713      * Show the specified panel.
38714      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38715      * @return {Roo.ContentPanel} The shown panel or null
38716      */
38717     showPanel : function(panel){
38718         panel = this.getPanel(panel);
38719         if(panel){
38720             this.setActivePanel(panel);
38721         }
38722         return panel;
38723     },
38724     
38725     /**
38726      * Get the active panel for this region.
38727      * @return {Roo.ContentPanel} The active panel or null
38728      */
38729     getActivePanel : function(){
38730         return this.activePanel;
38731     },
38732     
38733     /**
38734      * Add the passed ContentPanel(s)
38735      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38736      * @return {Roo.ContentPanel} The panel added (if only one was added)
38737      */
38738     add : function(panel){
38739         if(arguments.length > 1){
38740             for(var i = 0, len = arguments.length; i < len; i++) {
38741                 this.add(arguments[i]);
38742             }
38743             return null;
38744         }
38745         if(this.hasPanel(panel)){
38746             this.showPanel(panel);
38747             return panel;
38748         }
38749         var el = panel.getEl();
38750         if(el.dom.parentNode != this.mgr.el.dom){
38751             this.mgr.el.dom.appendChild(el.dom);
38752         }
38753         if(panel.setRegion){
38754             panel.setRegion(this);
38755         }
38756         this.panels.add(panel);
38757         el.setStyle("position", "absolute");
38758         if(!panel.background){
38759             this.setActivePanel(panel);
38760             if(this.config.initialSize && this.panels.getCount()==1){
38761                 this.resizeTo(this.config.initialSize);
38762             }
38763         }
38764         this.fireEvent("paneladded", this, panel);
38765         return panel;
38766     },
38767     
38768     /**
38769      * Returns true if the panel is in this region.
38770      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38771      * @return {Boolean}
38772      */
38773     hasPanel : function(panel){
38774         if(typeof panel == "object"){ // must be panel obj
38775             panel = panel.getId();
38776         }
38777         return this.getPanel(panel) ? true : false;
38778     },
38779     
38780     /**
38781      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38782      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38783      * @param {Boolean} preservePanel Overrides the config preservePanel option
38784      * @return {Roo.ContentPanel} The panel that was removed
38785      */
38786     remove : function(panel, preservePanel){
38787         panel = this.getPanel(panel);
38788         if(!panel){
38789             return null;
38790         }
38791         var e = {};
38792         this.fireEvent("beforeremove", this, panel, e);
38793         if(e.cancel === true){
38794             return null;
38795         }
38796         var panelId = panel.getId();
38797         this.panels.removeKey(panelId);
38798         return panel;
38799     },
38800     
38801     /**
38802      * Returns the panel specified or null if it's not in this region.
38803      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38804      * @return {Roo.ContentPanel}
38805      */
38806     getPanel : function(id){
38807         if(typeof id == "object"){ // must be panel obj
38808             return id;
38809         }
38810         return this.panels.get(id);
38811     },
38812     
38813     /**
38814      * Returns this regions position (north/south/east/west/center).
38815      * @return {String} 
38816      */
38817     getPosition: function(){
38818         return this.position;    
38819     }
38820 });/*
38821  * Based on:
38822  * Ext JS Library 1.1.1
38823  * Copyright(c) 2006-2007, Ext JS, LLC.
38824  *
38825  * Originally Released Under LGPL - original licence link has changed is not relivant.
38826  *
38827  * Fork - LGPL
38828  * <script type="text/javascript">
38829  */
38830  
38831 /**
38832  * @class Roo.bootstrap.layout.Region
38833  * @extends Roo.bootstrap.layout.Basic
38834  * This class represents a region in a layout manager.
38835  
38836  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38837  * @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})
38838  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38839  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38840  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38841  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38842  * @cfg {String}    title           The title for the region (overrides panel titles)
38843  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38844  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38845  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38846  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38847  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38848  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38849  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38850  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38851  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38852  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38853
38854  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38855  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38856  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38857  * @cfg {Number}    width           For East/West panels
38858  * @cfg {Number}    height          For North/South panels
38859  * @cfg {Boolean}   split           To show the splitter
38860  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38861  * 
38862  * @cfg {string}   cls             Extra CSS classes to add to region
38863  * 
38864  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38865  * @cfg {string}   region  the region that it inhabits..
38866  *
38867
38868  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38869  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38870
38871  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38872  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38873  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38874  */
38875 Roo.bootstrap.layout.Region = function(config)
38876 {
38877     this.applyConfig(config);
38878
38879     var mgr = config.mgr;
38880     var pos = config.region;
38881     config.skipConfig = true;
38882     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38883     
38884     if (mgr.el) {
38885         this.onRender(mgr.el);   
38886     }
38887      
38888     this.visible = true;
38889     this.collapsed = false;
38890     this.unrendered_panels = [];
38891 };
38892
38893 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38894
38895     position: '', // set by wrapper (eg. north/south etc..)
38896     unrendered_panels : null,  // unrendered panels.
38897     
38898     tabPosition : false,
38899     
38900     mgr: false, // points to 'Border'
38901     
38902     
38903     createBody : function(){
38904         /** This region's body element 
38905         * @type Roo.Element */
38906         this.bodyEl = this.el.createChild({
38907                 tag: "div",
38908                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38909         });
38910     },
38911
38912     onRender: function(ctr, pos)
38913     {
38914         var dh = Roo.DomHelper;
38915         /** This region's container element 
38916         * @type Roo.Element */
38917         this.el = dh.append(ctr.dom, {
38918                 tag: "div",
38919                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38920             }, true);
38921         /** This region's title element 
38922         * @type Roo.Element */
38923     
38924         this.titleEl = dh.append(this.el.dom,  {
38925                 tag: "div",
38926                 unselectable: "on",
38927                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38928                 children:[
38929                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38930                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38931                 ]
38932             }, true);
38933         
38934         this.titleEl.enableDisplayMode();
38935         /** This region's title text element 
38936         * @type HTMLElement */
38937         this.titleTextEl = this.titleEl.dom.firstChild;
38938         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38939         /*
38940         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38941         this.closeBtn.enableDisplayMode();
38942         this.closeBtn.on("click", this.closeClicked, this);
38943         this.closeBtn.hide();
38944     */
38945         this.createBody(this.config);
38946         if(this.config.hideWhenEmpty){
38947             this.hide();
38948             this.on("paneladded", this.validateVisibility, this);
38949             this.on("panelremoved", this.validateVisibility, this);
38950         }
38951         if(this.autoScroll){
38952             this.bodyEl.setStyle("overflow", "auto");
38953         }else{
38954             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38955         }
38956         //if(c.titlebar !== false){
38957             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38958                 this.titleEl.hide();
38959             }else{
38960                 this.titleEl.show();
38961                 if(this.config.title){
38962                     this.titleTextEl.innerHTML = this.config.title;
38963                 }
38964             }
38965         //}
38966         if(this.config.collapsed){
38967             this.collapse(true);
38968         }
38969         if(this.config.hidden){
38970             this.hide();
38971         }
38972         
38973         if (this.unrendered_panels && this.unrendered_panels.length) {
38974             for (var i =0;i< this.unrendered_panels.length; i++) {
38975                 this.add(this.unrendered_panels[i]);
38976             }
38977             this.unrendered_panels = null;
38978             
38979         }
38980         
38981     },
38982     
38983     applyConfig : function(c)
38984     {
38985         /*
38986          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38987             var dh = Roo.DomHelper;
38988             if(c.titlebar !== false){
38989                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38990                 this.collapseBtn.on("click", this.collapse, this);
38991                 this.collapseBtn.enableDisplayMode();
38992                 /*
38993                 if(c.showPin === true || this.showPin){
38994                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38995                     this.stickBtn.enableDisplayMode();
38996                     this.stickBtn.on("click", this.expand, this);
38997                     this.stickBtn.hide();
38998                 }
38999                 
39000             }
39001             */
39002             /** This region's collapsed element
39003             * @type Roo.Element */
39004             /*
39005              *
39006             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39007                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39008             ]}, true);
39009             
39010             if(c.floatable !== false){
39011                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39012                this.collapsedEl.on("click", this.collapseClick, this);
39013             }
39014
39015             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39016                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39017                    id: "message", unselectable: "on", style:{"float":"left"}});
39018                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39019              }
39020             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39021             this.expandBtn.on("click", this.expand, this);
39022             
39023         }
39024         
39025         if(this.collapseBtn){
39026             this.collapseBtn.setVisible(c.collapsible == true);
39027         }
39028         
39029         this.cmargins = c.cmargins || this.cmargins ||
39030                          (this.position == "west" || this.position == "east" ?
39031                              {top: 0, left: 2, right:2, bottom: 0} :
39032                              {top: 2, left: 0, right:0, bottom: 2});
39033         */
39034         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39035         
39036         
39037         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39038         
39039         this.autoScroll = c.autoScroll || false;
39040         
39041         
39042        
39043         
39044         this.duration = c.duration || .30;
39045         this.slideDuration = c.slideDuration || .45;
39046         this.config = c;
39047        
39048     },
39049     /**
39050      * Returns true if this region is currently visible.
39051      * @return {Boolean}
39052      */
39053     isVisible : function(){
39054         return this.visible;
39055     },
39056
39057     /**
39058      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39059      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39060      */
39061     //setCollapsedTitle : function(title){
39062     //    title = title || "&#160;";
39063      //   if(this.collapsedTitleTextEl){
39064       //      this.collapsedTitleTextEl.innerHTML = title;
39065        // }
39066     //},
39067
39068     getBox : function(){
39069         var b;
39070       //  if(!this.collapsed){
39071             b = this.el.getBox(false, true);
39072        // }else{
39073           //  b = this.collapsedEl.getBox(false, true);
39074         //}
39075         return b;
39076     },
39077
39078     getMargins : function(){
39079         return this.margins;
39080         //return this.collapsed ? this.cmargins : this.margins;
39081     },
39082 /*
39083     highlight : function(){
39084         this.el.addClass("x-layout-panel-dragover");
39085     },
39086
39087     unhighlight : function(){
39088         this.el.removeClass("x-layout-panel-dragover");
39089     },
39090 */
39091     updateBox : function(box)
39092     {
39093         if (!this.bodyEl) {
39094             return; // not rendered yet..
39095         }
39096         
39097         this.box = box;
39098         if(!this.collapsed){
39099             this.el.dom.style.left = box.x + "px";
39100             this.el.dom.style.top = box.y + "px";
39101             this.updateBody(box.width, box.height);
39102         }else{
39103             this.collapsedEl.dom.style.left = box.x + "px";
39104             this.collapsedEl.dom.style.top = box.y + "px";
39105             this.collapsedEl.setSize(box.width, box.height);
39106         }
39107         if(this.tabs){
39108             this.tabs.autoSizeTabs();
39109         }
39110     },
39111
39112     updateBody : function(w, h)
39113     {
39114         if(w !== null){
39115             this.el.setWidth(w);
39116             w -= this.el.getBorderWidth("rl");
39117             if(this.config.adjustments){
39118                 w += this.config.adjustments[0];
39119             }
39120         }
39121         if(h !== null && h > 0){
39122             this.el.setHeight(h);
39123             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39124             h -= this.el.getBorderWidth("tb");
39125             if(this.config.adjustments){
39126                 h += this.config.adjustments[1];
39127             }
39128             this.bodyEl.setHeight(h);
39129             if(this.tabs){
39130                 h = this.tabs.syncHeight(h);
39131             }
39132         }
39133         if(this.panelSize){
39134             w = w !== null ? w : this.panelSize.width;
39135             h = h !== null ? h : this.panelSize.height;
39136         }
39137         if(this.activePanel){
39138             var el = this.activePanel.getEl();
39139             w = w !== null ? w : el.getWidth();
39140             h = h !== null ? h : el.getHeight();
39141             this.panelSize = {width: w, height: h};
39142             this.activePanel.setSize(w, h);
39143         }
39144         if(Roo.isIE && this.tabs){
39145             this.tabs.el.repaint();
39146         }
39147     },
39148
39149     /**
39150      * Returns the container element for this region.
39151      * @return {Roo.Element}
39152      */
39153     getEl : function(){
39154         return this.el;
39155     },
39156
39157     /**
39158      * Hides this region.
39159      */
39160     hide : function(){
39161         //if(!this.collapsed){
39162             this.el.dom.style.left = "-2000px";
39163             this.el.hide();
39164         //}else{
39165          //   this.collapsedEl.dom.style.left = "-2000px";
39166          //   this.collapsedEl.hide();
39167        // }
39168         this.visible = false;
39169         this.fireEvent("visibilitychange", this, false);
39170     },
39171
39172     /**
39173      * Shows this region if it was previously hidden.
39174      */
39175     show : function(){
39176         //if(!this.collapsed){
39177             this.el.show();
39178         //}else{
39179         //    this.collapsedEl.show();
39180        // }
39181         this.visible = true;
39182         this.fireEvent("visibilitychange", this, true);
39183     },
39184 /*
39185     closeClicked : function(){
39186         if(this.activePanel){
39187             this.remove(this.activePanel);
39188         }
39189     },
39190
39191     collapseClick : function(e){
39192         if(this.isSlid){
39193            e.stopPropagation();
39194            this.slideIn();
39195         }else{
39196            e.stopPropagation();
39197            this.slideOut();
39198         }
39199     },
39200 */
39201     /**
39202      * Collapses this region.
39203      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39204      */
39205     /*
39206     collapse : function(skipAnim, skipCheck = false){
39207         if(this.collapsed) {
39208             return;
39209         }
39210         
39211         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39212             
39213             this.collapsed = true;
39214             if(this.split){
39215                 this.split.el.hide();
39216             }
39217             if(this.config.animate && skipAnim !== true){
39218                 this.fireEvent("invalidated", this);
39219                 this.animateCollapse();
39220             }else{
39221                 this.el.setLocation(-20000,-20000);
39222                 this.el.hide();
39223                 this.collapsedEl.show();
39224                 this.fireEvent("collapsed", this);
39225                 this.fireEvent("invalidated", this);
39226             }
39227         }
39228         
39229     },
39230 */
39231     animateCollapse : function(){
39232         // overridden
39233     },
39234
39235     /**
39236      * Expands this region if it was previously collapsed.
39237      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39238      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39239      */
39240     /*
39241     expand : function(e, skipAnim){
39242         if(e) {
39243             e.stopPropagation();
39244         }
39245         if(!this.collapsed || this.el.hasActiveFx()) {
39246             return;
39247         }
39248         if(this.isSlid){
39249             this.afterSlideIn();
39250             skipAnim = true;
39251         }
39252         this.collapsed = false;
39253         if(this.config.animate && skipAnim !== true){
39254             this.animateExpand();
39255         }else{
39256             this.el.show();
39257             if(this.split){
39258                 this.split.el.show();
39259             }
39260             this.collapsedEl.setLocation(-2000,-2000);
39261             this.collapsedEl.hide();
39262             this.fireEvent("invalidated", this);
39263             this.fireEvent("expanded", this);
39264         }
39265     },
39266 */
39267     animateExpand : function(){
39268         // overridden
39269     },
39270
39271     initTabs : function()
39272     {
39273         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39274         
39275         var ts = new Roo.bootstrap.panel.Tabs({
39276             el: this.bodyEl.dom,
39277             region : this,
39278             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39279             disableTooltips: this.config.disableTabTips,
39280             toolbar : this.config.toolbar
39281         });
39282         
39283         if(this.config.hideTabs){
39284             ts.stripWrap.setDisplayed(false);
39285         }
39286         this.tabs = ts;
39287         ts.resizeTabs = this.config.resizeTabs === true;
39288         ts.minTabWidth = this.config.minTabWidth || 40;
39289         ts.maxTabWidth = this.config.maxTabWidth || 250;
39290         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39291         ts.monitorResize = false;
39292         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39293         ts.bodyEl.addClass('roo-layout-tabs-body');
39294         this.panels.each(this.initPanelAsTab, this);
39295     },
39296
39297     initPanelAsTab : function(panel){
39298         var ti = this.tabs.addTab(
39299             panel.getEl().id,
39300             panel.getTitle(),
39301             null,
39302             this.config.closeOnTab && panel.isClosable(),
39303             panel.tpl
39304         );
39305         if(panel.tabTip !== undefined){
39306             ti.setTooltip(panel.tabTip);
39307         }
39308         ti.on("activate", function(){
39309               this.setActivePanel(panel);
39310         }, this);
39311         
39312         if(this.config.closeOnTab){
39313             ti.on("beforeclose", function(t, e){
39314                 e.cancel = true;
39315                 this.remove(panel);
39316             }, this);
39317         }
39318         
39319         panel.tabItem = ti;
39320         
39321         return ti;
39322     },
39323
39324     updatePanelTitle : function(panel, title)
39325     {
39326         if(this.activePanel == panel){
39327             this.updateTitle(title);
39328         }
39329         if(this.tabs){
39330             var ti = this.tabs.getTab(panel.getEl().id);
39331             ti.setText(title);
39332             if(panel.tabTip !== undefined){
39333                 ti.setTooltip(panel.tabTip);
39334             }
39335         }
39336     },
39337
39338     updateTitle : function(title){
39339         if(this.titleTextEl && !this.config.title){
39340             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39341         }
39342     },
39343
39344     setActivePanel : function(panel)
39345     {
39346         panel = this.getPanel(panel);
39347         if(this.activePanel && this.activePanel != panel){
39348             if(this.activePanel.setActiveState(false) === false){
39349                 return;
39350             }
39351         }
39352         this.activePanel = panel;
39353         panel.setActiveState(true);
39354         if(this.panelSize){
39355             panel.setSize(this.panelSize.width, this.panelSize.height);
39356         }
39357         if(this.closeBtn){
39358             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39359         }
39360         this.updateTitle(panel.getTitle());
39361         if(this.tabs){
39362             this.fireEvent("invalidated", this);
39363         }
39364         this.fireEvent("panelactivated", this, panel);
39365     },
39366
39367     /**
39368      * Shows the specified panel.
39369      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39370      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39371      */
39372     showPanel : function(panel)
39373     {
39374         panel = this.getPanel(panel);
39375         if(panel){
39376             if(this.tabs){
39377                 var tab = this.tabs.getTab(panel.getEl().id);
39378                 if(tab.isHidden()){
39379                     this.tabs.unhideTab(tab.id);
39380                 }
39381                 tab.activate();
39382             }else{
39383                 this.setActivePanel(panel);
39384             }
39385         }
39386         return panel;
39387     },
39388
39389     /**
39390      * Get the active panel for this region.
39391      * @return {Roo.ContentPanel} The active panel or null
39392      */
39393     getActivePanel : function(){
39394         return this.activePanel;
39395     },
39396
39397     validateVisibility : function(){
39398         if(this.panels.getCount() < 1){
39399             this.updateTitle("&#160;");
39400             this.closeBtn.hide();
39401             this.hide();
39402         }else{
39403             if(!this.isVisible()){
39404                 this.show();
39405             }
39406         }
39407     },
39408
39409     /**
39410      * Adds the passed ContentPanel(s) to this region.
39411      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39412      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39413      */
39414     add : function(panel)
39415     {
39416         if(arguments.length > 1){
39417             for(var i = 0, len = arguments.length; i < len; i++) {
39418                 this.add(arguments[i]);
39419             }
39420             return null;
39421         }
39422         
39423         // if we have not been rendered yet, then we can not really do much of this..
39424         if (!this.bodyEl) {
39425             this.unrendered_panels.push(panel);
39426             return panel;
39427         }
39428         
39429         
39430         
39431         
39432         if(this.hasPanel(panel)){
39433             this.showPanel(panel);
39434             return panel;
39435         }
39436         panel.setRegion(this);
39437         this.panels.add(panel);
39438        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39439             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39440             // and hide them... ???
39441             this.bodyEl.dom.appendChild(panel.getEl().dom);
39442             if(panel.background !== true){
39443                 this.setActivePanel(panel);
39444             }
39445             this.fireEvent("paneladded", this, panel);
39446             return panel;
39447         }
39448         */
39449         if(!this.tabs){
39450             this.initTabs();
39451         }else{
39452             this.initPanelAsTab(panel);
39453         }
39454         
39455         
39456         if(panel.background !== true){
39457             this.tabs.activate(panel.getEl().id);
39458         }
39459         this.fireEvent("paneladded", this, panel);
39460         return panel;
39461     },
39462
39463     /**
39464      * Hides the tab for the specified panel.
39465      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39466      */
39467     hidePanel : function(panel){
39468         if(this.tabs && (panel = this.getPanel(panel))){
39469             this.tabs.hideTab(panel.getEl().id);
39470         }
39471     },
39472
39473     /**
39474      * Unhides the tab for a previously hidden panel.
39475      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39476      */
39477     unhidePanel : function(panel){
39478         if(this.tabs && (panel = this.getPanel(panel))){
39479             this.tabs.unhideTab(panel.getEl().id);
39480         }
39481     },
39482
39483     clearPanels : function(){
39484         while(this.panels.getCount() > 0){
39485              this.remove(this.panels.first());
39486         }
39487     },
39488
39489     /**
39490      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39491      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39492      * @param {Boolean} preservePanel Overrides the config preservePanel option
39493      * @return {Roo.ContentPanel} The panel that was removed
39494      */
39495     remove : function(panel, preservePanel)
39496     {
39497         panel = this.getPanel(panel);
39498         if(!panel){
39499             return null;
39500         }
39501         var e = {};
39502         this.fireEvent("beforeremove", this, panel, e);
39503         if(e.cancel === true){
39504             return null;
39505         }
39506         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39507         var panelId = panel.getId();
39508         this.panels.removeKey(panelId);
39509         if(preservePanel){
39510             document.body.appendChild(panel.getEl().dom);
39511         }
39512         if(this.tabs){
39513             this.tabs.removeTab(panel.getEl().id);
39514         }else if (!preservePanel){
39515             this.bodyEl.dom.removeChild(panel.getEl().dom);
39516         }
39517         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39518             var p = this.panels.first();
39519             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39520             tempEl.appendChild(p.getEl().dom);
39521             this.bodyEl.update("");
39522             this.bodyEl.dom.appendChild(p.getEl().dom);
39523             tempEl = null;
39524             this.updateTitle(p.getTitle());
39525             this.tabs = null;
39526             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39527             this.setActivePanel(p);
39528         }
39529         panel.setRegion(null);
39530         if(this.activePanel == panel){
39531             this.activePanel = null;
39532         }
39533         if(this.config.autoDestroy !== false && preservePanel !== true){
39534             try{panel.destroy();}catch(e){}
39535         }
39536         this.fireEvent("panelremoved", this, panel);
39537         return panel;
39538     },
39539
39540     /**
39541      * Returns the TabPanel component used by this region
39542      * @return {Roo.TabPanel}
39543      */
39544     getTabs : function(){
39545         return this.tabs;
39546     },
39547
39548     createTool : function(parentEl, className){
39549         var btn = Roo.DomHelper.append(parentEl, {
39550             tag: "div",
39551             cls: "x-layout-tools-button",
39552             children: [ {
39553                 tag: "div",
39554                 cls: "roo-layout-tools-button-inner " + className,
39555                 html: "&#160;"
39556             }]
39557         }, true);
39558         btn.addClassOnOver("roo-layout-tools-button-over");
39559         return btn;
39560     }
39561 });/*
39562  * Based on:
39563  * Ext JS Library 1.1.1
39564  * Copyright(c) 2006-2007, Ext JS, LLC.
39565  *
39566  * Originally Released Under LGPL - original licence link has changed is not relivant.
39567  *
39568  * Fork - LGPL
39569  * <script type="text/javascript">
39570  */
39571  
39572
39573
39574 /**
39575  * @class Roo.SplitLayoutRegion
39576  * @extends Roo.LayoutRegion
39577  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39578  */
39579 Roo.bootstrap.layout.Split = function(config){
39580     this.cursor = config.cursor;
39581     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39582 };
39583
39584 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39585 {
39586     splitTip : "Drag to resize.",
39587     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39588     useSplitTips : false,
39589
39590     applyConfig : function(config){
39591         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39592     },
39593     
39594     onRender : function(ctr,pos) {
39595         
39596         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39597         if(!this.config.split){
39598             return;
39599         }
39600         if(!this.split){
39601             
39602             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39603                             tag: "div",
39604                             id: this.el.id + "-split",
39605                             cls: "roo-layout-split roo-layout-split-"+this.position,
39606                             html: "&#160;"
39607             });
39608             /** The SplitBar for this region 
39609             * @type Roo.SplitBar */
39610             // does not exist yet...
39611             Roo.log([this.position, this.orientation]);
39612             
39613             this.split = new Roo.bootstrap.SplitBar({
39614                 dragElement : splitEl,
39615                 resizingElement: this.el,
39616                 orientation : this.orientation
39617             });
39618             
39619             this.split.on("moved", this.onSplitMove, this);
39620             this.split.useShim = this.config.useShim === true;
39621             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39622             if(this.useSplitTips){
39623                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39624             }
39625             //if(config.collapsible){
39626             //    this.split.el.on("dblclick", this.collapse,  this);
39627             //}
39628         }
39629         if(typeof this.config.minSize != "undefined"){
39630             this.split.minSize = this.config.minSize;
39631         }
39632         if(typeof this.config.maxSize != "undefined"){
39633             this.split.maxSize = this.config.maxSize;
39634         }
39635         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39636             this.hideSplitter();
39637         }
39638         
39639     },
39640
39641     getHMaxSize : function(){
39642          var cmax = this.config.maxSize || 10000;
39643          var center = this.mgr.getRegion("center");
39644          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39645     },
39646
39647     getVMaxSize : function(){
39648          var cmax = this.config.maxSize || 10000;
39649          var center = this.mgr.getRegion("center");
39650          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39651     },
39652
39653     onSplitMove : function(split, newSize){
39654         this.fireEvent("resized", this, newSize);
39655     },
39656     
39657     /** 
39658      * Returns the {@link Roo.SplitBar} for this region.
39659      * @return {Roo.SplitBar}
39660      */
39661     getSplitBar : function(){
39662         return this.split;
39663     },
39664     
39665     hide : function(){
39666         this.hideSplitter();
39667         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39668     },
39669
39670     hideSplitter : function(){
39671         if(this.split){
39672             this.split.el.setLocation(-2000,-2000);
39673             this.split.el.hide();
39674         }
39675     },
39676
39677     show : function(){
39678         if(this.split){
39679             this.split.el.show();
39680         }
39681         Roo.bootstrap.layout.Split.superclass.show.call(this);
39682     },
39683     
39684     beforeSlide: function(){
39685         if(Roo.isGecko){// firefox overflow auto bug workaround
39686             this.bodyEl.clip();
39687             if(this.tabs) {
39688                 this.tabs.bodyEl.clip();
39689             }
39690             if(this.activePanel){
39691                 this.activePanel.getEl().clip();
39692                 
39693                 if(this.activePanel.beforeSlide){
39694                     this.activePanel.beforeSlide();
39695                 }
39696             }
39697         }
39698     },
39699     
39700     afterSlide : function(){
39701         if(Roo.isGecko){// firefox overflow auto bug workaround
39702             this.bodyEl.unclip();
39703             if(this.tabs) {
39704                 this.tabs.bodyEl.unclip();
39705             }
39706             if(this.activePanel){
39707                 this.activePanel.getEl().unclip();
39708                 if(this.activePanel.afterSlide){
39709                     this.activePanel.afterSlide();
39710                 }
39711             }
39712         }
39713     },
39714
39715     initAutoHide : function(){
39716         if(this.autoHide !== false){
39717             if(!this.autoHideHd){
39718                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39719                 this.autoHideHd = {
39720                     "mouseout": function(e){
39721                         if(!e.within(this.el, true)){
39722                             st.delay(500);
39723                         }
39724                     },
39725                     "mouseover" : function(e){
39726                         st.cancel();
39727                     },
39728                     scope : this
39729                 };
39730             }
39731             this.el.on(this.autoHideHd);
39732         }
39733     },
39734
39735     clearAutoHide : function(){
39736         if(this.autoHide !== false){
39737             this.el.un("mouseout", this.autoHideHd.mouseout);
39738             this.el.un("mouseover", this.autoHideHd.mouseover);
39739         }
39740     },
39741
39742     clearMonitor : function(){
39743         Roo.get(document).un("click", this.slideInIf, this);
39744     },
39745
39746     // these names are backwards but not changed for compat
39747     slideOut : function(){
39748         if(this.isSlid || this.el.hasActiveFx()){
39749             return;
39750         }
39751         this.isSlid = true;
39752         if(this.collapseBtn){
39753             this.collapseBtn.hide();
39754         }
39755         this.closeBtnState = this.closeBtn.getStyle('display');
39756         this.closeBtn.hide();
39757         if(this.stickBtn){
39758             this.stickBtn.show();
39759         }
39760         this.el.show();
39761         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39762         this.beforeSlide();
39763         this.el.setStyle("z-index", 10001);
39764         this.el.slideIn(this.getSlideAnchor(), {
39765             callback: function(){
39766                 this.afterSlide();
39767                 this.initAutoHide();
39768                 Roo.get(document).on("click", this.slideInIf, this);
39769                 this.fireEvent("slideshow", this);
39770             },
39771             scope: this,
39772             block: true
39773         });
39774     },
39775
39776     afterSlideIn : function(){
39777         this.clearAutoHide();
39778         this.isSlid = false;
39779         this.clearMonitor();
39780         this.el.setStyle("z-index", "");
39781         if(this.collapseBtn){
39782             this.collapseBtn.show();
39783         }
39784         this.closeBtn.setStyle('display', this.closeBtnState);
39785         if(this.stickBtn){
39786             this.stickBtn.hide();
39787         }
39788         this.fireEvent("slidehide", this);
39789     },
39790
39791     slideIn : function(cb){
39792         if(!this.isSlid || this.el.hasActiveFx()){
39793             Roo.callback(cb);
39794             return;
39795         }
39796         this.isSlid = false;
39797         this.beforeSlide();
39798         this.el.slideOut(this.getSlideAnchor(), {
39799             callback: function(){
39800                 this.el.setLeftTop(-10000, -10000);
39801                 this.afterSlide();
39802                 this.afterSlideIn();
39803                 Roo.callback(cb);
39804             },
39805             scope: this,
39806             block: true
39807         });
39808     },
39809     
39810     slideInIf : function(e){
39811         if(!e.within(this.el)){
39812             this.slideIn();
39813         }
39814     },
39815
39816     animateCollapse : function(){
39817         this.beforeSlide();
39818         this.el.setStyle("z-index", 20000);
39819         var anchor = this.getSlideAnchor();
39820         this.el.slideOut(anchor, {
39821             callback : function(){
39822                 this.el.setStyle("z-index", "");
39823                 this.collapsedEl.slideIn(anchor, {duration:.3});
39824                 this.afterSlide();
39825                 this.el.setLocation(-10000,-10000);
39826                 this.el.hide();
39827                 this.fireEvent("collapsed", this);
39828             },
39829             scope: this,
39830             block: true
39831         });
39832     },
39833
39834     animateExpand : function(){
39835         this.beforeSlide();
39836         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39837         this.el.setStyle("z-index", 20000);
39838         this.collapsedEl.hide({
39839             duration:.1
39840         });
39841         this.el.slideIn(this.getSlideAnchor(), {
39842             callback : function(){
39843                 this.el.setStyle("z-index", "");
39844                 this.afterSlide();
39845                 if(this.split){
39846                     this.split.el.show();
39847                 }
39848                 this.fireEvent("invalidated", this);
39849                 this.fireEvent("expanded", this);
39850             },
39851             scope: this,
39852             block: true
39853         });
39854     },
39855
39856     anchors : {
39857         "west" : "left",
39858         "east" : "right",
39859         "north" : "top",
39860         "south" : "bottom"
39861     },
39862
39863     sanchors : {
39864         "west" : "l",
39865         "east" : "r",
39866         "north" : "t",
39867         "south" : "b"
39868     },
39869
39870     canchors : {
39871         "west" : "tl-tr",
39872         "east" : "tr-tl",
39873         "north" : "tl-bl",
39874         "south" : "bl-tl"
39875     },
39876
39877     getAnchor : function(){
39878         return this.anchors[this.position];
39879     },
39880
39881     getCollapseAnchor : function(){
39882         return this.canchors[this.position];
39883     },
39884
39885     getSlideAnchor : function(){
39886         return this.sanchors[this.position];
39887     },
39888
39889     getAlignAdj : function(){
39890         var cm = this.cmargins;
39891         switch(this.position){
39892             case "west":
39893                 return [0, 0];
39894             break;
39895             case "east":
39896                 return [0, 0];
39897             break;
39898             case "north":
39899                 return [0, 0];
39900             break;
39901             case "south":
39902                 return [0, 0];
39903             break;
39904         }
39905     },
39906
39907     getExpandAdj : function(){
39908         var c = this.collapsedEl, cm = this.cmargins;
39909         switch(this.position){
39910             case "west":
39911                 return [-(cm.right+c.getWidth()+cm.left), 0];
39912             break;
39913             case "east":
39914                 return [cm.right+c.getWidth()+cm.left, 0];
39915             break;
39916             case "north":
39917                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39918             break;
39919             case "south":
39920                 return [0, cm.top+cm.bottom+c.getHeight()];
39921             break;
39922         }
39923     }
39924 });/*
39925  * Based on:
39926  * Ext JS Library 1.1.1
39927  * Copyright(c) 2006-2007, Ext JS, LLC.
39928  *
39929  * Originally Released Under LGPL - original licence link has changed is not relivant.
39930  *
39931  * Fork - LGPL
39932  * <script type="text/javascript">
39933  */
39934 /*
39935  * These classes are private internal classes
39936  */
39937 Roo.bootstrap.layout.Center = function(config){
39938     config.region = "center";
39939     Roo.bootstrap.layout.Region.call(this, config);
39940     this.visible = true;
39941     this.minWidth = config.minWidth || 20;
39942     this.minHeight = config.minHeight || 20;
39943 };
39944
39945 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39946     hide : function(){
39947         // center panel can't be hidden
39948     },
39949     
39950     show : function(){
39951         // center panel can't be hidden
39952     },
39953     
39954     getMinWidth: function(){
39955         return this.minWidth;
39956     },
39957     
39958     getMinHeight: function(){
39959         return this.minHeight;
39960     }
39961 });
39962
39963
39964
39965
39966  
39967
39968
39969
39970
39971
39972
39973 Roo.bootstrap.layout.North = function(config)
39974 {
39975     config.region = 'north';
39976     config.cursor = 'n-resize';
39977     
39978     Roo.bootstrap.layout.Split.call(this, config);
39979     
39980     
39981     if(this.split){
39982         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39983         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39984         this.split.el.addClass("roo-layout-split-v");
39985     }
39986     //var size = config.initialSize || config.height;
39987     //if(this.el && typeof size != "undefined"){
39988     //    this.el.setHeight(size);
39989     //}
39990 };
39991 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39992 {
39993     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39994      
39995      
39996     onRender : function(ctr, pos)
39997     {
39998         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39999         var size = this.config.initialSize || this.config.height;
40000         if(this.el && typeof size != "undefined"){
40001             this.el.setHeight(size);
40002         }
40003     
40004     },
40005     
40006     getBox : function(){
40007         if(this.collapsed){
40008             return this.collapsedEl.getBox();
40009         }
40010         var box = this.el.getBox();
40011         if(this.split){
40012             box.height += this.split.el.getHeight();
40013         }
40014         return box;
40015     },
40016     
40017     updateBox : function(box){
40018         if(this.split && !this.collapsed){
40019             box.height -= this.split.el.getHeight();
40020             this.split.el.setLeft(box.x);
40021             this.split.el.setTop(box.y+box.height);
40022             this.split.el.setWidth(box.width);
40023         }
40024         if(this.collapsed){
40025             this.updateBody(box.width, null);
40026         }
40027         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40028     }
40029 });
40030
40031
40032
40033
40034
40035 Roo.bootstrap.layout.South = function(config){
40036     config.region = 'south';
40037     config.cursor = 's-resize';
40038     Roo.bootstrap.layout.Split.call(this, config);
40039     if(this.split){
40040         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40041         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40042         this.split.el.addClass("roo-layout-split-v");
40043     }
40044     
40045 };
40046
40047 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40048     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40049     
40050     onRender : function(ctr, pos)
40051     {
40052         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40053         var size = this.config.initialSize || this.config.height;
40054         if(this.el && typeof size != "undefined"){
40055             this.el.setHeight(size);
40056         }
40057     
40058     },
40059     
40060     getBox : function(){
40061         if(this.collapsed){
40062             return this.collapsedEl.getBox();
40063         }
40064         var box = this.el.getBox();
40065         if(this.split){
40066             var sh = this.split.el.getHeight();
40067             box.height += sh;
40068             box.y -= sh;
40069         }
40070         return box;
40071     },
40072     
40073     updateBox : function(box){
40074         if(this.split && !this.collapsed){
40075             var sh = this.split.el.getHeight();
40076             box.height -= sh;
40077             box.y += sh;
40078             this.split.el.setLeft(box.x);
40079             this.split.el.setTop(box.y-sh);
40080             this.split.el.setWidth(box.width);
40081         }
40082         if(this.collapsed){
40083             this.updateBody(box.width, null);
40084         }
40085         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40086     }
40087 });
40088
40089 Roo.bootstrap.layout.East = function(config){
40090     config.region = "east";
40091     config.cursor = "e-resize";
40092     Roo.bootstrap.layout.Split.call(this, config);
40093     if(this.split){
40094         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40095         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40096         this.split.el.addClass("roo-layout-split-h");
40097     }
40098     
40099 };
40100 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40101     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40102     
40103     onRender : function(ctr, pos)
40104     {
40105         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40106         var size = this.config.initialSize || this.config.width;
40107         if(this.el && typeof size != "undefined"){
40108             this.el.setWidth(size);
40109         }
40110     
40111     },
40112     
40113     getBox : function(){
40114         if(this.collapsed){
40115             return this.collapsedEl.getBox();
40116         }
40117         var box = this.el.getBox();
40118         if(this.split){
40119             var sw = this.split.el.getWidth();
40120             box.width += sw;
40121             box.x -= sw;
40122         }
40123         return box;
40124     },
40125
40126     updateBox : function(box){
40127         if(this.split && !this.collapsed){
40128             var sw = this.split.el.getWidth();
40129             box.width -= sw;
40130             this.split.el.setLeft(box.x);
40131             this.split.el.setTop(box.y);
40132             this.split.el.setHeight(box.height);
40133             box.x += sw;
40134         }
40135         if(this.collapsed){
40136             this.updateBody(null, box.height);
40137         }
40138         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40139     }
40140 });
40141
40142 Roo.bootstrap.layout.West = function(config){
40143     config.region = "west";
40144     config.cursor = "w-resize";
40145     
40146     Roo.bootstrap.layout.Split.call(this, config);
40147     if(this.split){
40148         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40149         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40150         this.split.el.addClass("roo-layout-split-h");
40151     }
40152     
40153 };
40154 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40155     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40156     
40157     onRender: function(ctr, pos)
40158     {
40159         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40160         var size = this.config.initialSize || this.config.width;
40161         if(typeof size != "undefined"){
40162             this.el.setWidth(size);
40163         }
40164     },
40165     
40166     getBox : function(){
40167         if(this.collapsed){
40168             return this.collapsedEl.getBox();
40169         }
40170         var box = this.el.getBox();
40171         if (box.width == 0) {
40172             box.width = this.config.width; // kludge?
40173         }
40174         if(this.split){
40175             box.width += this.split.el.getWidth();
40176         }
40177         return box;
40178     },
40179     
40180     updateBox : function(box){
40181         if(this.split && !this.collapsed){
40182             var sw = this.split.el.getWidth();
40183             box.width -= sw;
40184             this.split.el.setLeft(box.x+box.width);
40185             this.split.el.setTop(box.y);
40186             this.split.el.setHeight(box.height);
40187         }
40188         if(this.collapsed){
40189             this.updateBody(null, box.height);
40190         }
40191         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40192     }
40193 });Roo.namespace("Roo.bootstrap.panel");/*
40194  * Based on:
40195  * Ext JS Library 1.1.1
40196  * Copyright(c) 2006-2007, Ext JS, LLC.
40197  *
40198  * Originally Released Under LGPL - original licence link has changed is not relivant.
40199  *
40200  * Fork - LGPL
40201  * <script type="text/javascript">
40202  */
40203 /**
40204  * @class Roo.bootstrap.paenl.Content
40205  * @extends Roo.util.Observable
40206  * @builder-top
40207  * @children Roo.bootstrap.Component
40208  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40209  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40210  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40211  * @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
40212  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40213  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40214  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40215  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40216  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40217  * @cfg {String} title          The title for this panel
40218  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40219  * @cfg {String} url            Calls {@link #setUrl} with this value
40220  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40221  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40222  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40223  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40224  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40225  * @cfg {Boolean} badges render the badges
40226  * @cfg {String} cls  extra classes to use  
40227  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40228  
40229  * @constructor
40230  * Create a new ContentPanel.
40231  * @param {String/Object} config A string to set only the title or a config object
40232  
40233  */
40234 Roo.bootstrap.panel.Content = function( config){
40235     
40236     this.tpl = config.tpl || false;
40237     
40238     var el = config.el;
40239     var content = config.content;
40240
40241     if(config.autoCreate){ // xtype is available if this is called from factory
40242         el = Roo.id();
40243     }
40244     this.el = Roo.get(el);
40245     if(!this.el && config && config.autoCreate){
40246         if(typeof config.autoCreate == "object"){
40247             if(!config.autoCreate.id){
40248                 config.autoCreate.id = config.id||el;
40249             }
40250             this.el = Roo.DomHelper.append(document.body,
40251                         config.autoCreate, true);
40252         }else{
40253             var elcfg =  {
40254                 tag: "div",
40255                 cls: (config.cls || '') +
40256                     (config.background ? ' bg-' + config.background : '') +
40257                     " roo-layout-inactive-content",
40258                 id: config.id||el
40259             };
40260             if (config.iframe) {
40261                 elcfg.cn = [
40262                     {
40263                         tag : 'iframe',
40264                         style : 'border: 0px',
40265                         src : 'about:blank'
40266                     }
40267                 ];
40268             }
40269               
40270             if (config.html) {
40271                 elcfg.html = config.html;
40272                 
40273             }
40274                         
40275             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40276             if (config.iframe) {
40277                 this.iframeEl = this.el.select('iframe',true).first();
40278             }
40279             
40280         }
40281     } 
40282     this.closable = false;
40283     this.loaded = false;
40284     this.active = false;
40285    
40286       
40287     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40288         
40289         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40290         
40291         this.wrapEl = this.el; //this.el.wrap();
40292         var ti = [];
40293         if (config.toolbar.items) {
40294             ti = config.toolbar.items ;
40295             delete config.toolbar.items ;
40296         }
40297         
40298         var nitems = [];
40299         this.toolbar.render(this.wrapEl, 'before');
40300         for(var i =0;i < ti.length;i++) {
40301           //  Roo.log(['add child', items[i]]);
40302             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40303         }
40304         this.toolbar.items = nitems;
40305         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40306         delete config.toolbar;
40307         
40308     }
40309     /*
40310     // xtype created footer. - not sure if will work as we normally have to render first..
40311     if (this.footer && !this.footer.el && this.footer.xtype) {
40312         if (!this.wrapEl) {
40313             this.wrapEl = this.el.wrap();
40314         }
40315     
40316         this.footer.container = this.wrapEl.createChild();
40317          
40318         this.footer = Roo.factory(this.footer, Roo);
40319         
40320     }
40321     */
40322     
40323      if(typeof config == "string"){
40324         this.title = config;
40325     }else{
40326         Roo.apply(this, config);
40327     }
40328     
40329     if(this.resizeEl){
40330         this.resizeEl = Roo.get(this.resizeEl, true);
40331     }else{
40332         this.resizeEl = this.el;
40333     }
40334     // handle view.xtype
40335     
40336  
40337     
40338     
40339     this.addEvents({
40340         /**
40341          * @event activate
40342          * Fires when this panel is activated. 
40343          * @param {Roo.ContentPanel} this
40344          */
40345         "activate" : true,
40346         /**
40347          * @event deactivate
40348          * Fires when this panel is activated. 
40349          * @param {Roo.ContentPanel} this
40350          */
40351         "deactivate" : true,
40352
40353         /**
40354          * @event resize
40355          * Fires when this panel is resized if fitToFrame is true.
40356          * @param {Roo.ContentPanel} this
40357          * @param {Number} width The width after any component adjustments
40358          * @param {Number} height The height after any component adjustments
40359          */
40360         "resize" : true,
40361         
40362          /**
40363          * @event render
40364          * Fires when this tab is created
40365          * @param {Roo.ContentPanel} this
40366          */
40367         "render" : true,
40368         
40369           /**
40370          * @event scroll
40371          * Fires when this content is scrolled
40372          * @param {Roo.ContentPanel} this
40373          * @param {Event} scrollEvent
40374          */
40375         "scroll" : true
40376         
40377         
40378         
40379     });
40380     
40381
40382     
40383     
40384     if(this.autoScroll && !this.iframe){
40385         this.resizeEl.setStyle("overflow", "auto");
40386         this.resizeEl.on('scroll', this.onScroll, this);
40387     } else {
40388         // fix randome scrolling
40389         //this.el.on('scroll', function() {
40390         //    Roo.log('fix random scolling');
40391         //    this.scrollTo('top',0); 
40392         //});
40393     }
40394     content = content || this.content;
40395     if(content){
40396         this.setContent(content);
40397     }
40398     if(config && config.url){
40399         this.setUrl(this.url, this.params, this.loadOnce);
40400     }
40401     
40402     
40403     
40404     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40405     
40406     if (this.view && typeof(this.view.xtype) != 'undefined') {
40407         this.view.el = this.el.appendChild(document.createElement("div"));
40408         this.view = Roo.factory(this.view); 
40409         this.view.render  &&  this.view.render(false, '');  
40410     }
40411     
40412     
40413     this.fireEvent('render', this);
40414 };
40415
40416 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40417     
40418     cls : '',
40419     background : '',
40420     
40421     tabTip : '',
40422     
40423     iframe : false,
40424     iframeEl : false,
40425     
40426     /* Resize Element - use this to work out scroll etc. */
40427     resizeEl : false,
40428     
40429     setRegion : function(region){
40430         this.region = region;
40431         this.setActiveClass(region && !this.background);
40432     },
40433     
40434     
40435     setActiveClass: function(state)
40436     {
40437         if(state){
40438            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40439            this.el.setStyle('position','relative');
40440         }else{
40441            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40442            this.el.setStyle('position', 'absolute');
40443         } 
40444     },
40445     
40446     /**
40447      * Returns the toolbar for this Panel if one was configured. 
40448      * @return {Roo.Toolbar} 
40449      */
40450     getToolbar : function(){
40451         return this.toolbar;
40452     },
40453     
40454     setActiveState : function(active)
40455     {
40456         this.active = active;
40457         this.setActiveClass(active);
40458         if(!active){
40459             if(this.fireEvent("deactivate", this) === false){
40460                 return false;
40461             }
40462             return true;
40463         }
40464         this.fireEvent("activate", this);
40465         return true;
40466     },
40467     /**
40468      * Updates this panel's element (not for iframe)
40469      * @param {String} content The new content
40470      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40471     */
40472     setContent : function(content, loadScripts){
40473         if (this.iframe) {
40474             return;
40475         }
40476         
40477         this.el.update(content, loadScripts);
40478     },
40479
40480     ignoreResize : function(w, h){
40481         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40482             return true;
40483         }else{
40484             this.lastSize = {width: w, height: h};
40485             return false;
40486         }
40487     },
40488     /**
40489      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40490      * @return {Roo.UpdateManager} The UpdateManager
40491      */
40492     getUpdateManager : function(){
40493         if (this.iframe) {
40494             return false;
40495         }
40496         return this.el.getUpdateManager();
40497     },
40498      /**
40499      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40500      * Does not work with IFRAME contents
40501      * @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:
40502 <pre><code>
40503 panel.load({
40504     url: "your-url.php",
40505     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40506     callback: yourFunction,
40507     scope: yourObject, //(optional scope)
40508     discardUrl: false,
40509     nocache: false,
40510     text: "Loading...",
40511     timeout: 30,
40512     scripts: false
40513 });
40514 </code></pre>
40515      
40516      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40517      * 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.
40518      * @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}
40519      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40520      * @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.
40521      * @return {Roo.ContentPanel} this
40522      */
40523     load : function(){
40524         
40525         if (this.iframe) {
40526             return this;
40527         }
40528         
40529         var um = this.el.getUpdateManager();
40530         um.update.apply(um, arguments);
40531         return this;
40532     },
40533
40534
40535     /**
40536      * 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.
40537      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40538      * @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)
40539      * @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)
40540      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40541      */
40542     setUrl : function(url, params, loadOnce){
40543         if (this.iframe) {
40544             this.iframeEl.dom.src = url;
40545             return false;
40546         }
40547         
40548         if(this.refreshDelegate){
40549             this.removeListener("activate", this.refreshDelegate);
40550         }
40551         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40552         this.on("activate", this.refreshDelegate);
40553         return this.el.getUpdateManager();
40554     },
40555     
40556     _handleRefresh : function(url, params, loadOnce){
40557         if(!loadOnce || !this.loaded){
40558             var updater = this.el.getUpdateManager();
40559             updater.update(url, params, this._setLoaded.createDelegate(this));
40560         }
40561     },
40562     
40563     _setLoaded : function(){
40564         this.loaded = true;
40565     }, 
40566     
40567     /**
40568      * Returns this panel's id
40569      * @return {String} 
40570      */
40571     getId : function(){
40572         return this.el.id;
40573     },
40574     
40575     /** 
40576      * Returns this panel's element - used by regiosn to add.
40577      * @return {Roo.Element} 
40578      */
40579     getEl : function(){
40580         return this.wrapEl || this.el;
40581     },
40582     
40583    
40584     
40585     adjustForComponents : function(width, height)
40586     {
40587         //Roo.log('adjustForComponents ');
40588         if(this.resizeEl != this.el){
40589             width -= this.el.getFrameWidth('lr');
40590             height -= this.el.getFrameWidth('tb');
40591         }
40592         if(this.toolbar){
40593             var te = this.toolbar.getEl();
40594             te.setWidth(width);
40595             height -= te.getHeight();
40596         }
40597         if(this.footer){
40598             var te = this.footer.getEl();
40599             te.setWidth(width);
40600             height -= te.getHeight();
40601         }
40602         
40603         
40604         if(this.adjustments){
40605             width += this.adjustments[0];
40606             height += this.adjustments[1];
40607         }
40608         return {"width": width, "height": height};
40609     },
40610     
40611     setSize : function(width, height){
40612         if(this.fitToFrame && !this.ignoreResize(width, height)){
40613             if(this.fitContainer && this.resizeEl != this.el){
40614                 this.el.setSize(width, height);
40615             }
40616             var size = this.adjustForComponents(width, height);
40617             if (this.iframe) {
40618                 this.iframeEl.setSize(width,height);
40619             }
40620             
40621             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40622             this.fireEvent('resize', this, size.width, size.height);
40623             
40624             
40625         }
40626     },
40627     
40628     /**
40629      * Returns this panel's title
40630      * @return {String} 
40631      */
40632     getTitle : function(){
40633         
40634         if (typeof(this.title) != 'object') {
40635             return this.title;
40636         }
40637         
40638         var t = '';
40639         for (var k in this.title) {
40640             if (!this.title.hasOwnProperty(k)) {
40641                 continue;
40642             }
40643             
40644             if (k.indexOf('-') >= 0) {
40645                 var s = k.split('-');
40646                 for (var i = 0; i<s.length; i++) {
40647                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40648                 }
40649             } else {
40650                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40651             }
40652         }
40653         return t;
40654     },
40655     
40656     /**
40657      * Set this panel's title
40658      * @param {String} title
40659      */
40660     setTitle : function(title){
40661         this.title = title;
40662         if(this.region){
40663             this.region.updatePanelTitle(this, title);
40664         }
40665     },
40666     
40667     /**
40668      * Returns true is this panel was configured to be closable
40669      * @return {Boolean} 
40670      */
40671     isClosable : function(){
40672         return this.closable;
40673     },
40674     
40675     beforeSlide : function(){
40676         this.el.clip();
40677         this.resizeEl.clip();
40678     },
40679     
40680     afterSlide : function(){
40681         this.el.unclip();
40682         this.resizeEl.unclip();
40683     },
40684     
40685     /**
40686      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40687      *   Will fail silently if the {@link #setUrl} method has not been called.
40688      *   This does not activate the panel, just updates its content.
40689      */
40690     refresh : function(){
40691         if(this.refreshDelegate){
40692            this.loaded = false;
40693            this.refreshDelegate();
40694         }
40695     },
40696     
40697     /**
40698      * Destroys this panel
40699      */
40700     destroy : function(){
40701         this.el.removeAllListeners();
40702         var tempEl = document.createElement("span");
40703         tempEl.appendChild(this.el.dom);
40704         tempEl.innerHTML = "";
40705         this.el.remove();
40706         this.el = null;
40707     },
40708     
40709     /**
40710      * form - if the content panel contains a form - this is a reference to it.
40711      * @type {Roo.form.Form}
40712      */
40713     form : false,
40714     /**
40715      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40716      *    This contains a reference to it.
40717      * @type {Roo.View}
40718      */
40719     view : false,
40720     
40721       /**
40722      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40723      * <pre><code>
40724
40725 layout.addxtype({
40726        xtype : 'Form',
40727        items: [ .... ]
40728    }
40729 );
40730
40731 </code></pre>
40732      * @param {Object} cfg Xtype definition of item to add.
40733      */
40734     
40735     
40736     getChildContainer: function () {
40737         return this.getEl();
40738     },
40739     
40740     
40741     onScroll : function(e)
40742     {
40743         this.fireEvent('scroll', this, e);
40744     }
40745     
40746     
40747     /*
40748         var  ret = new Roo.factory(cfg);
40749         return ret;
40750         
40751         
40752         // add form..
40753         if (cfg.xtype.match(/^Form$/)) {
40754             
40755             var el;
40756             //if (this.footer) {
40757             //    el = this.footer.container.insertSibling(false, 'before');
40758             //} else {
40759                 el = this.el.createChild();
40760             //}
40761
40762             this.form = new  Roo.form.Form(cfg);
40763             
40764             
40765             if ( this.form.allItems.length) {
40766                 this.form.render(el.dom);
40767             }
40768             return this.form;
40769         }
40770         // should only have one of theses..
40771         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40772             // views.. should not be just added - used named prop 'view''
40773             
40774             cfg.el = this.el.appendChild(document.createElement("div"));
40775             // factory?
40776             
40777             var ret = new Roo.factory(cfg);
40778              
40779              ret.render && ret.render(false, ''); // render blank..
40780             this.view = ret;
40781             return ret;
40782         }
40783         return false;
40784     }
40785     \*/
40786 });
40787  
40788 /**
40789  * @class Roo.bootstrap.panel.Grid
40790  * @extends Roo.bootstrap.panel.Content
40791  * @constructor
40792  * Create a new GridPanel.
40793  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40794  * @param {Object} config A the config object
40795   
40796  */
40797
40798
40799
40800 Roo.bootstrap.panel.Grid = function(config)
40801 {
40802     
40803       
40804     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40805         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40806
40807     config.el = this.wrapper;
40808     //this.el = this.wrapper;
40809     
40810       if (config.container) {
40811         // ctor'ed from a Border/panel.grid
40812         
40813         
40814         this.wrapper.setStyle("overflow", "hidden");
40815         this.wrapper.addClass('roo-grid-container');
40816
40817     }
40818     
40819     
40820     if(config.toolbar){
40821         var tool_el = this.wrapper.createChild();    
40822         this.toolbar = Roo.factory(config.toolbar);
40823         var ti = [];
40824         if (config.toolbar.items) {
40825             ti = config.toolbar.items ;
40826             delete config.toolbar.items ;
40827         }
40828         
40829         var nitems = [];
40830         this.toolbar.render(tool_el);
40831         for(var i =0;i < ti.length;i++) {
40832           //  Roo.log(['add child', items[i]]);
40833             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40834         }
40835         this.toolbar.items = nitems;
40836         
40837         delete config.toolbar;
40838     }
40839     
40840     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40841     config.grid.scrollBody = true;;
40842     config.grid.monitorWindowResize = false; // turn off autosizing
40843     config.grid.autoHeight = false;
40844     config.grid.autoWidth = false;
40845     
40846     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40847     
40848     if (config.background) {
40849         // render grid on panel activation (if panel background)
40850         this.on('activate', function(gp) {
40851             if (!gp.grid.rendered) {
40852                 gp.grid.render(this.wrapper);
40853                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40854             }
40855         });
40856             
40857     } else {
40858         this.grid.render(this.wrapper);
40859         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40860
40861     }
40862     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40863     // ??? needed ??? config.el = this.wrapper;
40864     
40865     
40866     
40867   
40868     // xtype created footer. - not sure if will work as we normally have to render first..
40869     if (this.footer && !this.footer.el && this.footer.xtype) {
40870         
40871         var ctr = this.grid.getView().getFooterPanel(true);
40872         this.footer.dataSource = this.grid.dataSource;
40873         this.footer = Roo.factory(this.footer, Roo);
40874         this.footer.render(ctr);
40875         
40876     }
40877     
40878     
40879     
40880     
40881      
40882 };
40883
40884 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40885     getId : function(){
40886         return this.grid.id;
40887     },
40888     
40889     /**
40890      * Returns the grid for this panel
40891      * @return {Roo.bootstrap.Table} 
40892      */
40893     getGrid : function(){
40894         return this.grid;    
40895     },
40896     
40897     setSize : function(width, height){
40898         if(!this.ignoreResize(width, height)){
40899             var grid = this.grid;
40900             var size = this.adjustForComponents(width, height);
40901             // tfoot is not a footer?
40902           
40903             
40904             var gridel = grid.getGridEl();
40905             gridel.setSize(size.width, size.height);
40906             
40907             var tbd = grid.getGridEl().select('tbody', true).first();
40908             var thd = grid.getGridEl().select('thead',true).first();
40909             var tbf= grid.getGridEl().select('tfoot', true).first();
40910
40911             if (tbf) {
40912                 size.height -= tbf.getHeight();
40913             }
40914             if (thd) {
40915                 size.height -= thd.getHeight();
40916             }
40917             
40918             tbd.setSize(size.width, size.height );
40919             // this is for the account management tab -seems to work there.
40920             var thd = grid.getGridEl().select('thead',true).first();
40921             //if (tbd) {
40922             //    tbd.setSize(size.width, size.height - thd.getHeight());
40923             //}
40924              
40925             grid.autoSize();
40926         }
40927     },
40928      
40929     
40930     
40931     beforeSlide : function(){
40932         this.grid.getView().scroller.clip();
40933     },
40934     
40935     afterSlide : function(){
40936         this.grid.getView().scroller.unclip();
40937     },
40938     
40939     destroy : function(){
40940         this.grid.destroy();
40941         delete this.grid;
40942         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40943     }
40944 });
40945
40946 /**
40947  * @class Roo.bootstrap.panel.Nest
40948  * @extends Roo.bootstrap.panel.Content
40949  * @constructor
40950  * Create a new Panel, that can contain a layout.Border.
40951  * 
40952  * 
40953  * @param {Roo.BorderLayout} layout The layout for this panel
40954  * @param {String/Object} config A string to set only the title or a config object
40955  */
40956 Roo.bootstrap.panel.Nest = function(config)
40957 {
40958     // construct with only one argument..
40959     /* FIXME - implement nicer consturctors
40960     if (layout.layout) {
40961         config = layout;
40962         layout = config.layout;
40963         delete config.layout;
40964     }
40965     if (layout.xtype && !layout.getEl) {
40966         // then layout needs constructing..
40967         layout = Roo.factory(layout, Roo);
40968     }
40969     */
40970     
40971     config.el =  config.layout.getEl();
40972     
40973     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40974     
40975     config.layout.monitorWindowResize = false; // turn off autosizing
40976     this.layout = config.layout;
40977     this.layout.getEl().addClass("roo-layout-nested-layout");
40978     this.layout.parent = this;
40979     
40980     
40981     
40982     
40983 };
40984
40985 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40986
40987     setSize : function(width, height){
40988         if(!this.ignoreResize(width, height)){
40989             var size = this.adjustForComponents(width, height);
40990             var el = this.layout.getEl();
40991             if (size.height < 1) {
40992                 el.setWidth(size.width);   
40993             } else {
40994                 el.setSize(size.width, size.height);
40995             }
40996             var touch = el.dom.offsetWidth;
40997             this.layout.layout();
40998             // ie requires a double layout on the first pass
40999             if(Roo.isIE && !this.initialized){
41000                 this.initialized = true;
41001                 this.layout.layout();
41002             }
41003         }
41004     },
41005     
41006     // activate all subpanels if not currently active..
41007     
41008     setActiveState : function(active){
41009         this.active = active;
41010         this.setActiveClass(active);
41011         
41012         if(!active){
41013             this.fireEvent("deactivate", this);
41014             return;
41015         }
41016         
41017         this.fireEvent("activate", this);
41018         // not sure if this should happen before or after..
41019         if (!this.layout) {
41020             return; // should not happen..
41021         }
41022         var reg = false;
41023         for (var r in this.layout.regions) {
41024             reg = this.layout.getRegion(r);
41025             if (reg.getActivePanel()) {
41026                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41027                 reg.setActivePanel(reg.getActivePanel());
41028                 continue;
41029             }
41030             if (!reg.panels.length) {
41031                 continue;
41032             }
41033             reg.showPanel(reg.getPanel(0));
41034         }
41035         
41036         
41037         
41038         
41039     },
41040     
41041     /**
41042      * Returns the nested BorderLayout for this panel
41043      * @return {Roo.BorderLayout} 
41044      */
41045     getLayout : function(){
41046         return this.layout;
41047     },
41048     
41049      /**
41050      * Adds a xtype elements to the layout of the nested panel
41051      * <pre><code>
41052
41053 panel.addxtype({
41054        xtype : 'ContentPanel',
41055        region: 'west',
41056        items: [ .... ]
41057    }
41058 );
41059
41060 panel.addxtype({
41061         xtype : 'NestedLayoutPanel',
41062         region: 'west',
41063         layout: {
41064            center: { },
41065            west: { }   
41066         },
41067         items : [ ... list of content panels or nested layout panels.. ]
41068    }
41069 );
41070 </code></pre>
41071      * @param {Object} cfg Xtype definition of item to add.
41072      */
41073     addxtype : function(cfg) {
41074         return this.layout.addxtype(cfg);
41075     
41076     }
41077 });/*
41078  * Based on:
41079  * Ext JS Library 1.1.1
41080  * Copyright(c) 2006-2007, Ext JS, LLC.
41081  *
41082  * Originally Released Under LGPL - original licence link has changed is not relivant.
41083  *
41084  * Fork - LGPL
41085  * <script type="text/javascript">
41086  */
41087 /**
41088  * @class Roo.TabPanel
41089  * @extends Roo.util.Observable
41090  * A lightweight tab container.
41091  * <br><br>
41092  * Usage:
41093  * <pre><code>
41094 // basic tabs 1, built from existing content
41095 var tabs = new Roo.TabPanel("tabs1");
41096 tabs.addTab("script", "View Script");
41097 tabs.addTab("markup", "View Markup");
41098 tabs.activate("script");
41099
41100 // more advanced tabs, built from javascript
41101 var jtabs = new Roo.TabPanel("jtabs");
41102 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41103
41104 // set up the UpdateManager
41105 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41106 var updater = tab2.getUpdateManager();
41107 updater.setDefaultUrl("ajax1.htm");
41108 tab2.on('activate', updater.refresh, updater, true);
41109
41110 // Use setUrl for Ajax loading
41111 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41112 tab3.setUrl("ajax2.htm", null, true);
41113
41114 // Disabled tab
41115 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41116 tab4.disable();
41117
41118 jtabs.activate("jtabs-1");
41119  * </code></pre>
41120  * @constructor
41121  * Create a new TabPanel.
41122  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41123  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41124  */
41125 Roo.bootstrap.panel.Tabs = function(config){
41126     /**
41127     * The container element for this TabPanel.
41128     * @type Roo.Element
41129     */
41130     this.el = Roo.get(config.el);
41131     delete config.el;
41132     if(config){
41133         if(typeof config == "boolean"){
41134             this.tabPosition = config ? "bottom" : "top";
41135         }else{
41136             Roo.apply(this, config);
41137         }
41138     }
41139     
41140     if(this.tabPosition == "bottom"){
41141         // if tabs are at the bottom = create the body first.
41142         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41143         this.el.addClass("roo-tabs-bottom");
41144     }
41145     // next create the tabs holders
41146     
41147     if (this.tabPosition == "west"){
41148         
41149         var reg = this.region; // fake it..
41150         while (reg) {
41151             if (!reg.mgr.parent) {
41152                 break;
41153             }
41154             reg = reg.mgr.parent.region;
41155         }
41156         Roo.log("got nest?");
41157         Roo.log(reg);
41158         if (reg.mgr.getRegion('west')) {
41159             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41160             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41161             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41162             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41163             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41164         
41165             
41166         }
41167         
41168         
41169     } else {
41170      
41171         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41172         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41173         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41174         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41175     }
41176     
41177     
41178     if(Roo.isIE){
41179         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41180     }
41181     
41182     // finally - if tabs are at the top, then create the body last..
41183     if(this.tabPosition != "bottom"){
41184         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41185          * @type Roo.Element
41186          */
41187         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41188         this.el.addClass("roo-tabs-top");
41189     }
41190     this.items = [];
41191
41192     this.bodyEl.setStyle("position", "relative");
41193
41194     this.active = null;
41195     this.activateDelegate = this.activate.createDelegate(this);
41196
41197     this.addEvents({
41198         /**
41199          * @event tabchange
41200          * Fires when the active tab changes
41201          * @param {Roo.TabPanel} this
41202          * @param {Roo.TabPanelItem} activePanel The new active tab
41203          */
41204         "tabchange": true,
41205         /**
41206          * @event beforetabchange
41207          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41208          * @param {Roo.TabPanel} this
41209          * @param {Object} e Set cancel to true on this object to cancel the tab change
41210          * @param {Roo.TabPanelItem} tab The tab being changed to
41211          */
41212         "beforetabchange" : true
41213     });
41214
41215     Roo.EventManager.onWindowResize(this.onResize, this);
41216     this.cpad = this.el.getPadding("lr");
41217     this.hiddenCount = 0;
41218
41219
41220     // toolbar on the tabbar support...
41221     if (this.toolbar) {
41222         alert("no toolbar support yet");
41223         this.toolbar  = false;
41224         /*
41225         var tcfg = this.toolbar;
41226         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41227         this.toolbar = new Roo.Toolbar(tcfg);
41228         if (Roo.isSafari) {
41229             var tbl = tcfg.container.child('table', true);
41230             tbl.setAttribute('width', '100%');
41231         }
41232         */
41233         
41234     }
41235    
41236
41237
41238     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41239 };
41240
41241 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41242     /*
41243      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41244      */
41245     tabPosition : "top",
41246     /*
41247      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41248      */
41249     currentTabWidth : 0,
41250     /*
41251      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41252      */
41253     minTabWidth : 40,
41254     /*
41255      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41256      */
41257     maxTabWidth : 250,
41258     /*
41259      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41260      */
41261     preferredTabWidth : 175,
41262     /*
41263      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41264      */
41265     resizeTabs : false,
41266     /*
41267      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41268      */
41269     monitorResize : true,
41270     /*
41271      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41272      */
41273     toolbar : false,  // set by caller..
41274     
41275     region : false, /// set by caller
41276     
41277     disableTooltips : true, // not used yet...
41278
41279     /**
41280      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41281      * @param {String} id The id of the div to use <b>or create</b>
41282      * @param {String} text The text for the tab
41283      * @param {String} content (optional) Content to put in the TabPanelItem body
41284      * @param {Boolean} closable (optional) True to create a close icon on the tab
41285      * @return {Roo.TabPanelItem} The created TabPanelItem
41286      */
41287     addTab : function(id, text, content, closable, tpl)
41288     {
41289         var item = new Roo.bootstrap.panel.TabItem({
41290             panel: this,
41291             id : id,
41292             text : text,
41293             closable : closable,
41294             tpl : tpl
41295         });
41296         this.addTabItem(item);
41297         if(content){
41298             item.setContent(content);
41299         }
41300         return item;
41301     },
41302
41303     /**
41304      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41305      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41306      * @return {Roo.TabPanelItem}
41307      */
41308     getTab : function(id){
41309         return this.items[id];
41310     },
41311
41312     /**
41313      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41314      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41315      */
41316     hideTab : function(id){
41317         var t = this.items[id];
41318         if(!t.isHidden()){
41319            t.setHidden(true);
41320            this.hiddenCount++;
41321            this.autoSizeTabs();
41322         }
41323     },
41324
41325     /**
41326      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41327      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41328      */
41329     unhideTab : function(id){
41330         var t = this.items[id];
41331         if(t.isHidden()){
41332            t.setHidden(false);
41333            this.hiddenCount--;
41334            this.autoSizeTabs();
41335         }
41336     },
41337
41338     /**
41339      * Adds an existing {@link Roo.TabPanelItem}.
41340      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41341      */
41342     addTabItem : function(item)
41343     {
41344         this.items[item.id] = item;
41345         this.items.push(item);
41346         this.autoSizeTabs();
41347       //  if(this.resizeTabs){
41348     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41349   //         this.autoSizeTabs();
41350 //        }else{
41351 //            item.autoSize();
41352        // }
41353     },
41354
41355     /**
41356      * Removes a {@link Roo.TabPanelItem}.
41357      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41358      */
41359     removeTab : function(id){
41360         var items = this.items;
41361         var tab = items[id];
41362         if(!tab) { return; }
41363         var index = items.indexOf(tab);
41364         if(this.active == tab && items.length > 1){
41365             var newTab = this.getNextAvailable(index);
41366             if(newTab) {
41367                 newTab.activate();
41368             }
41369         }
41370         this.stripEl.dom.removeChild(tab.pnode.dom);
41371         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41372             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41373         }
41374         items.splice(index, 1);
41375         delete this.items[tab.id];
41376         tab.fireEvent("close", tab);
41377         tab.purgeListeners();
41378         this.autoSizeTabs();
41379     },
41380
41381     getNextAvailable : function(start){
41382         var items = this.items;
41383         var index = start;
41384         // look for a next tab that will slide over to
41385         // replace the one being removed
41386         while(index < items.length){
41387             var item = items[++index];
41388             if(item && !item.isHidden()){
41389                 return item;
41390             }
41391         }
41392         // if one isn't found select the previous tab (on the left)
41393         index = start;
41394         while(index >= 0){
41395             var item = items[--index];
41396             if(item && !item.isHidden()){
41397                 return item;
41398             }
41399         }
41400         return null;
41401     },
41402
41403     /**
41404      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41405      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41406      */
41407     disableTab : function(id){
41408         var tab = this.items[id];
41409         if(tab && this.active != tab){
41410             tab.disable();
41411         }
41412     },
41413
41414     /**
41415      * Enables a {@link Roo.TabPanelItem} that is disabled.
41416      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41417      */
41418     enableTab : function(id){
41419         var tab = this.items[id];
41420         tab.enable();
41421     },
41422
41423     /**
41424      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41425      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41426      * @return {Roo.TabPanelItem} The TabPanelItem.
41427      */
41428     activate : function(id)
41429     {
41430         //Roo.log('activite:'  + id);
41431         
41432         var tab = this.items[id];
41433         if(!tab){
41434             return null;
41435         }
41436         if(tab == this.active || tab.disabled){
41437             return tab;
41438         }
41439         var e = {};
41440         this.fireEvent("beforetabchange", this, e, tab);
41441         if(e.cancel !== true && !tab.disabled){
41442             if(this.active){
41443                 this.active.hide();
41444             }
41445             this.active = this.items[id];
41446             this.active.show();
41447             this.fireEvent("tabchange", this, this.active);
41448         }
41449         return tab;
41450     },
41451
41452     /**
41453      * Gets the active {@link Roo.TabPanelItem}.
41454      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41455      */
41456     getActiveTab : function(){
41457         return this.active;
41458     },
41459
41460     /**
41461      * Updates the tab body element to fit the height of the container element
41462      * for overflow scrolling
41463      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41464      */
41465     syncHeight : function(targetHeight){
41466         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41467         var bm = this.bodyEl.getMargins();
41468         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41469         this.bodyEl.setHeight(newHeight);
41470         return newHeight;
41471     },
41472
41473     onResize : function(){
41474         if(this.monitorResize){
41475             this.autoSizeTabs();
41476         }
41477     },
41478
41479     /**
41480      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41481      */
41482     beginUpdate : function(){
41483         this.updating = true;
41484     },
41485
41486     /**
41487      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41488      */
41489     endUpdate : function(){
41490         this.updating = false;
41491         this.autoSizeTabs();
41492     },
41493
41494     /**
41495      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41496      */
41497     autoSizeTabs : function()
41498     {
41499         var count = this.items.length;
41500         var vcount = count - this.hiddenCount;
41501         
41502         if (vcount < 2) {
41503             this.stripEl.hide();
41504         } else {
41505             this.stripEl.show();
41506         }
41507         
41508         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41509             return;
41510         }
41511         
41512         
41513         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41514         var availWidth = Math.floor(w / vcount);
41515         var b = this.stripBody;
41516         if(b.getWidth() > w){
41517             var tabs = this.items;
41518             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41519             if(availWidth < this.minTabWidth){
41520                 /*if(!this.sleft){    // incomplete scrolling code
41521                     this.createScrollButtons();
41522                 }
41523                 this.showScroll();
41524                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41525             }
41526         }else{
41527             if(this.currentTabWidth < this.preferredTabWidth){
41528                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41529             }
41530         }
41531     },
41532
41533     /**
41534      * Returns the number of tabs in this TabPanel.
41535      * @return {Number}
41536      */
41537      getCount : function(){
41538          return this.items.length;
41539      },
41540
41541     /**
41542      * Resizes all the tabs to the passed width
41543      * @param {Number} The new width
41544      */
41545     setTabWidth : function(width){
41546         this.currentTabWidth = width;
41547         for(var i = 0, len = this.items.length; i < len; i++) {
41548                 if(!this.items[i].isHidden()) {
41549                 this.items[i].setWidth(width);
41550             }
41551         }
41552     },
41553
41554     /**
41555      * Destroys this TabPanel
41556      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41557      */
41558     destroy : function(removeEl){
41559         Roo.EventManager.removeResizeListener(this.onResize, this);
41560         for(var i = 0, len = this.items.length; i < len; i++){
41561             this.items[i].purgeListeners();
41562         }
41563         if(removeEl === true){
41564             this.el.update("");
41565             this.el.remove();
41566         }
41567     },
41568     
41569     createStrip : function(container)
41570     {
41571         var strip = document.createElement("nav");
41572         strip.className = Roo.bootstrap.version == 4 ?
41573             "navbar-light bg-light" : 
41574             "navbar navbar-default"; //"x-tabs-wrap";
41575         container.appendChild(strip);
41576         return strip;
41577     },
41578     
41579     createStripList : function(strip)
41580     {
41581         // div wrapper for retard IE
41582         // returns the "tr" element.
41583         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41584         //'<div class="x-tabs-strip-wrap">'+
41585           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41586           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41587         return strip.firstChild; //.firstChild.firstChild.firstChild;
41588     },
41589     createBody : function(container)
41590     {
41591         var body = document.createElement("div");
41592         Roo.id(body, "tab-body");
41593         //Roo.fly(body).addClass("x-tabs-body");
41594         Roo.fly(body).addClass("tab-content");
41595         container.appendChild(body);
41596         return body;
41597     },
41598     createItemBody :function(bodyEl, id){
41599         var body = Roo.getDom(id);
41600         if(!body){
41601             body = document.createElement("div");
41602             body.id = id;
41603         }
41604         //Roo.fly(body).addClass("x-tabs-item-body");
41605         Roo.fly(body).addClass("tab-pane");
41606          bodyEl.insertBefore(body, bodyEl.firstChild);
41607         return body;
41608     },
41609     /** @private */
41610     createStripElements :  function(stripEl, text, closable, tpl)
41611     {
41612         var td = document.createElement("li"); // was td..
41613         td.className = 'nav-item';
41614         
41615         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41616         
41617         
41618         stripEl.appendChild(td);
41619         /*if(closable){
41620             td.className = "x-tabs-closable";
41621             if(!this.closeTpl){
41622                 this.closeTpl = new Roo.Template(
41623                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41624                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41625                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41626                 );
41627             }
41628             var el = this.closeTpl.overwrite(td, {"text": text});
41629             var close = el.getElementsByTagName("div")[0];
41630             var inner = el.getElementsByTagName("em")[0];
41631             return {"el": el, "close": close, "inner": inner};
41632         } else {
41633         */
41634         // not sure what this is..
41635 //            if(!this.tabTpl){
41636                 //this.tabTpl = new Roo.Template(
41637                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41638                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41639                 //);
41640 //                this.tabTpl = new Roo.Template(
41641 //                   '<a href="#">' +
41642 //                   '<span unselectable="on"' +
41643 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41644 //                            ' >{text}</span></a>'
41645 //                );
41646 //                
41647 //            }
41648
41649
41650             var template = tpl || this.tabTpl || false;
41651             
41652             if(!template){
41653                 template =  new Roo.Template(
41654                         Roo.bootstrap.version == 4 ? 
41655                             (
41656                                 '<a class="nav-link" href="#" unselectable="on"' +
41657                                      (this.disableTooltips ? '' : ' title="{text}"') +
41658                                      ' >{text}</a>'
41659                             ) : (
41660                                 '<a class="nav-link" href="#">' +
41661                                 '<span unselectable="on"' +
41662                                          (this.disableTooltips ? '' : ' title="{text}"') +
41663                                     ' >{text}</span></a>'
41664                             )
41665                 );
41666             }
41667             
41668             switch (typeof(template)) {
41669                 case 'object' :
41670                     break;
41671                 case 'string' :
41672                     template = new Roo.Template(template);
41673                     break;
41674                 default :
41675                     break;
41676             }
41677             
41678             var el = template.overwrite(td, {"text": text});
41679             
41680             var inner = el.getElementsByTagName("span")[0];
41681             
41682             return {"el": el, "inner": inner};
41683             
41684     }
41685         
41686     
41687 });
41688
41689 /**
41690  * @class Roo.TabPanelItem
41691  * @extends Roo.util.Observable
41692  * Represents an individual item (tab plus body) in a TabPanel.
41693  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41694  * @param {String} id The id of this TabPanelItem
41695  * @param {String} text The text for the tab of this TabPanelItem
41696  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41697  */
41698 Roo.bootstrap.panel.TabItem = function(config){
41699     /**
41700      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41701      * @type Roo.TabPanel
41702      */
41703     this.tabPanel = config.panel;
41704     /**
41705      * The id for this TabPanelItem
41706      * @type String
41707      */
41708     this.id = config.id;
41709     /** @private */
41710     this.disabled = false;
41711     /** @private */
41712     this.text = config.text;
41713     /** @private */
41714     this.loaded = false;
41715     this.closable = config.closable;
41716
41717     /**
41718      * The body element for this TabPanelItem.
41719      * @type Roo.Element
41720      */
41721     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41722     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41723     this.bodyEl.setStyle("display", "block");
41724     this.bodyEl.setStyle("zoom", "1");
41725     //this.hideAction();
41726
41727     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41728     /** @private */
41729     this.el = Roo.get(els.el);
41730     this.inner = Roo.get(els.inner, true);
41731      this.textEl = Roo.bootstrap.version == 4 ?
41732         this.el : Roo.get(this.el.dom.firstChild, true);
41733
41734     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41735     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41736
41737     
41738 //    this.el.on("mousedown", this.onTabMouseDown, this);
41739     this.el.on("click", this.onTabClick, this);
41740     /** @private */
41741     if(config.closable){
41742         var c = Roo.get(els.close, true);
41743         c.dom.title = this.closeText;
41744         c.addClassOnOver("close-over");
41745         c.on("click", this.closeClick, this);
41746      }
41747
41748     this.addEvents({
41749          /**
41750          * @event activate
41751          * Fires when this tab becomes the active tab.
41752          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41753          * @param {Roo.TabPanelItem} this
41754          */
41755         "activate": true,
41756         /**
41757          * @event beforeclose
41758          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41759          * @param {Roo.TabPanelItem} this
41760          * @param {Object} e Set cancel to true on this object to cancel the close.
41761          */
41762         "beforeclose": true,
41763         /**
41764          * @event close
41765          * Fires when this tab is closed.
41766          * @param {Roo.TabPanelItem} this
41767          */
41768          "close": true,
41769         /**
41770          * @event deactivate
41771          * Fires when this tab is no longer the active tab.
41772          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41773          * @param {Roo.TabPanelItem} this
41774          */
41775          "deactivate" : true
41776     });
41777     this.hidden = false;
41778
41779     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41780 };
41781
41782 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41783            {
41784     purgeListeners : function(){
41785        Roo.util.Observable.prototype.purgeListeners.call(this);
41786        this.el.removeAllListeners();
41787     },
41788     /**
41789      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41790      */
41791     show : function(){
41792         this.status_node.addClass("active");
41793         this.showAction();
41794         if(Roo.isOpera){
41795             this.tabPanel.stripWrap.repaint();
41796         }
41797         this.fireEvent("activate", this.tabPanel, this);
41798     },
41799
41800     /**
41801      * Returns true if this tab is the active tab.
41802      * @return {Boolean}
41803      */
41804     isActive : function(){
41805         return this.tabPanel.getActiveTab() == this;
41806     },
41807
41808     /**
41809      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41810      */
41811     hide : function(){
41812         this.status_node.removeClass("active");
41813         this.hideAction();
41814         this.fireEvent("deactivate", this.tabPanel, this);
41815     },
41816
41817     hideAction : function(){
41818         this.bodyEl.hide();
41819         this.bodyEl.setStyle("position", "absolute");
41820         this.bodyEl.setLeft("-20000px");
41821         this.bodyEl.setTop("-20000px");
41822     },
41823
41824     showAction : function(){
41825         this.bodyEl.setStyle("position", "relative");
41826         this.bodyEl.setTop("");
41827         this.bodyEl.setLeft("");
41828         this.bodyEl.show();
41829     },
41830
41831     /**
41832      * Set the tooltip for the tab.
41833      * @param {String} tooltip The tab's tooltip
41834      */
41835     setTooltip : function(text){
41836         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41837             this.textEl.dom.qtip = text;
41838             this.textEl.dom.removeAttribute('title');
41839         }else{
41840             this.textEl.dom.title = text;
41841         }
41842     },
41843
41844     onTabClick : function(e){
41845         e.preventDefault();
41846         this.tabPanel.activate(this.id);
41847     },
41848
41849     onTabMouseDown : function(e){
41850         e.preventDefault();
41851         this.tabPanel.activate(this.id);
41852     },
41853 /*
41854     getWidth : function(){
41855         return this.inner.getWidth();
41856     },
41857
41858     setWidth : function(width){
41859         var iwidth = width - this.linode.getPadding("lr");
41860         this.inner.setWidth(iwidth);
41861         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41862         this.linode.setWidth(width);
41863     },
41864 */
41865     /**
41866      * Show or hide the tab
41867      * @param {Boolean} hidden True to hide or false to show.
41868      */
41869     setHidden : function(hidden){
41870         this.hidden = hidden;
41871         this.linode.setStyle("display", hidden ? "none" : "");
41872     },
41873
41874     /**
41875      * Returns true if this tab is "hidden"
41876      * @return {Boolean}
41877      */
41878     isHidden : function(){
41879         return this.hidden;
41880     },
41881
41882     /**
41883      * Returns the text for this tab
41884      * @return {String}
41885      */
41886     getText : function(){
41887         return this.text;
41888     },
41889     /*
41890     autoSize : function(){
41891         //this.el.beginMeasure();
41892         this.textEl.setWidth(1);
41893         /*
41894          *  #2804 [new] Tabs in Roojs
41895          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41896          */
41897         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41898         //this.el.endMeasure();
41899     //},
41900
41901     /**
41902      * Sets the text for the tab (Note: this also sets the tooltip text)
41903      * @param {String} text The tab's text and tooltip
41904      */
41905     setText : function(text){
41906         this.text = text;
41907         this.textEl.update(text);
41908         this.setTooltip(text);
41909         //if(!this.tabPanel.resizeTabs){
41910         //    this.autoSize();
41911         //}
41912     },
41913     /**
41914      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41915      */
41916     activate : function(){
41917         this.tabPanel.activate(this.id);
41918     },
41919
41920     /**
41921      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41922      */
41923     disable : function(){
41924         if(this.tabPanel.active != this){
41925             this.disabled = true;
41926             this.status_node.addClass("disabled");
41927         }
41928     },
41929
41930     /**
41931      * Enables this TabPanelItem if it was previously disabled.
41932      */
41933     enable : function(){
41934         this.disabled = false;
41935         this.status_node.removeClass("disabled");
41936     },
41937
41938     /**
41939      * Sets the content for this TabPanelItem.
41940      * @param {String} content The content
41941      * @param {Boolean} loadScripts true to look for and load scripts
41942      */
41943     setContent : function(content, loadScripts){
41944         this.bodyEl.update(content, loadScripts);
41945     },
41946
41947     /**
41948      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41949      * @return {Roo.UpdateManager} The UpdateManager
41950      */
41951     getUpdateManager : function(){
41952         return this.bodyEl.getUpdateManager();
41953     },
41954
41955     /**
41956      * Set a URL to be used to load the content for this TabPanelItem.
41957      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41958      * @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)
41959      * @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)
41960      * @return {Roo.UpdateManager} The UpdateManager
41961      */
41962     setUrl : function(url, params, loadOnce){
41963         if(this.refreshDelegate){
41964             this.un('activate', this.refreshDelegate);
41965         }
41966         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41967         this.on("activate", this.refreshDelegate);
41968         return this.bodyEl.getUpdateManager();
41969     },
41970
41971     /** @private */
41972     _handleRefresh : function(url, params, loadOnce){
41973         if(!loadOnce || !this.loaded){
41974             var updater = this.bodyEl.getUpdateManager();
41975             updater.update(url, params, this._setLoaded.createDelegate(this));
41976         }
41977     },
41978
41979     /**
41980      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41981      *   Will fail silently if the setUrl method has not been called.
41982      *   This does not activate the panel, just updates its content.
41983      */
41984     refresh : function(){
41985         if(this.refreshDelegate){
41986            this.loaded = false;
41987            this.refreshDelegate();
41988         }
41989     },
41990
41991     /** @private */
41992     _setLoaded : function(){
41993         this.loaded = true;
41994     },
41995
41996     /** @private */
41997     closeClick : function(e){
41998         var o = {};
41999         e.stopEvent();
42000         this.fireEvent("beforeclose", this, o);
42001         if(o.cancel !== true){
42002             this.tabPanel.removeTab(this.id);
42003         }
42004     },
42005     /**
42006      * The text displayed in the tooltip for the close icon.
42007      * @type String
42008      */
42009     closeText : "Close this tab"
42010 });
42011 /**
42012 *    This script refer to:
42013 *    Title: International Telephone Input
42014 *    Author: Jack O'Connor
42015 *    Code version:  v12.1.12
42016 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42017 **/
42018
42019 Roo.bootstrap.PhoneInputData = function() {
42020     var d = [
42021       [
42022         "Afghanistan (‫افغانستان‬‎)",
42023         "af",
42024         "93"
42025       ],
42026       [
42027         "Albania (Shqipëri)",
42028         "al",
42029         "355"
42030       ],
42031       [
42032         "Algeria (‫الجزائر‬‎)",
42033         "dz",
42034         "213"
42035       ],
42036       [
42037         "American Samoa",
42038         "as",
42039         "1684"
42040       ],
42041       [
42042         "Andorra",
42043         "ad",
42044         "376"
42045       ],
42046       [
42047         "Angola",
42048         "ao",
42049         "244"
42050       ],
42051       [
42052         "Anguilla",
42053         "ai",
42054         "1264"
42055       ],
42056       [
42057         "Antigua and Barbuda",
42058         "ag",
42059         "1268"
42060       ],
42061       [
42062         "Argentina",
42063         "ar",
42064         "54"
42065       ],
42066       [
42067         "Armenia (Հայաստան)",
42068         "am",
42069         "374"
42070       ],
42071       [
42072         "Aruba",
42073         "aw",
42074         "297"
42075       ],
42076       [
42077         "Australia",
42078         "au",
42079         "61",
42080         0
42081       ],
42082       [
42083         "Austria (Österreich)",
42084         "at",
42085         "43"
42086       ],
42087       [
42088         "Azerbaijan (Azərbaycan)",
42089         "az",
42090         "994"
42091       ],
42092       [
42093         "Bahamas",
42094         "bs",
42095         "1242"
42096       ],
42097       [
42098         "Bahrain (‫البحرين‬‎)",
42099         "bh",
42100         "973"
42101       ],
42102       [
42103         "Bangladesh (বাংলাদেশ)",
42104         "bd",
42105         "880"
42106       ],
42107       [
42108         "Barbados",
42109         "bb",
42110         "1246"
42111       ],
42112       [
42113         "Belarus (Беларусь)",
42114         "by",
42115         "375"
42116       ],
42117       [
42118         "Belgium (België)",
42119         "be",
42120         "32"
42121       ],
42122       [
42123         "Belize",
42124         "bz",
42125         "501"
42126       ],
42127       [
42128         "Benin (Bénin)",
42129         "bj",
42130         "229"
42131       ],
42132       [
42133         "Bermuda",
42134         "bm",
42135         "1441"
42136       ],
42137       [
42138         "Bhutan (འབྲུག)",
42139         "bt",
42140         "975"
42141       ],
42142       [
42143         "Bolivia",
42144         "bo",
42145         "591"
42146       ],
42147       [
42148         "Bosnia and Herzegovina (Босна и Херцеговина)",
42149         "ba",
42150         "387"
42151       ],
42152       [
42153         "Botswana",
42154         "bw",
42155         "267"
42156       ],
42157       [
42158         "Brazil (Brasil)",
42159         "br",
42160         "55"
42161       ],
42162       [
42163         "British Indian Ocean Territory",
42164         "io",
42165         "246"
42166       ],
42167       [
42168         "British Virgin Islands",
42169         "vg",
42170         "1284"
42171       ],
42172       [
42173         "Brunei",
42174         "bn",
42175         "673"
42176       ],
42177       [
42178         "Bulgaria (България)",
42179         "bg",
42180         "359"
42181       ],
42182       [
42183         "Burkina Faso",
42184         "bf",
42185         "226"
42186       ],
42187       [
42188         "Burundi (Uburundi)",
42189         "bi",
42190         "257"
42191       ],
42192       [
42193         "Cambodia (កម្ពុជា)",
42194         "kh",
42195         "855"
42196       ],
42197       [
42198         "Cameroon (Cameroun)",
42199         "cm",
42200         "237"
42201       ],
42202       [
42203         "Canada",
42204         "ca",
42205         "1",
42206         1,
42207         ["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"]
42208       ],
42209       [
42210         "Cape Verde (Kabu Verdi)",
42211         "cv",
42212         "238"
42213       ],
42214       [
42215         "Caribbean Netherlands",
42216         "bq",
42217         "599",
42218         1
42219       ],
42220       [
42221         "Cayman Islands",
42222         "ky",
42223         "1345"
42224       ],
42225       [
42226         "Central African Republic (République centrafricaine)",
42227         "cf",
42228         "236"
42229       ],
42230       [
42231         "Chad (Tchad)",
42232         "td",
42233         "235"
42234       ],
42235       [
42236         "Chile",
42237         "cl",
42238         "56"
42239       ],
42240       [
42241         "China (中国)",
42242         "cn",
42243         "86"
42244       ],
42245       [
42246         "Christmas Island",
42247         "cx",
42248         "61",
42249         2
42250       ],
42251       [
42252         "Cocos (Keeling) Islands",
42253         "cc",
42254         "61",
42255         1
42256       ],
42257       [
42258         "Colombia",
42259         "co",
42260         "57"
42261       ],
42262       [
42263         "Comoros (‫جزر القمر‬‎)",
42264         "km",
42265         "269"
42266       ],
42267       [
42268         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42269         "cd",
42270         "243"
42271       ],
42272       [
42273         "Congo (Republic) (Congo-Brazzaville)",
42274         "cg",
42275         "242"
42276       ],
42277       [
42278         "Cook Islands",
42279         "ck",
42280         "682"
42281       ],
42282       [
42283         "Costa Rica",
42284         "cr",
42285         "506"
42286       ],
42287       [
42288         "Côte d’Ivoire",
42289         "ci",
42290         "225"
42291       ],
42292       [
42293         "Croatia (Hrvatska)",
42294         "hr",
42295         "385"
42296       ],
42297       [
42298         "Cuba",
42299         "cu",
42300         "53"
42301       ],
42302       [
42303         "Curaçao",
42304         "cw",
42305         "599",
42306         0
42307       ],
42308       [
42309         "Cyprus (Κύπρος)",
42310         "cy",
42311         "357"
42312       ],
42313       [
42314         "Czech Republic (Česká republika)",
42315         "cz",
42316         "420"
42317       ],
42318       [
42319         "Denmark (Danmark)",
42320         "dk",
42321         "45"
42322       ],
42323       [
42324         "Djibouti",
42325         "dj",
42326         "253"
42327       ],
42328       [
42329         "Dominica",
42330         "dm",
42331         "1767"
42332       ],
42333       [
42334         "Dominican Republic (República Dominicana)",
42335         "do",
42336         "1",
42337         2,
42338         ["809", "829", "849"]
42339       ],
42340       [
42341         "Ecuador",
42342         "ec",
42343         "593"
42344       ],
42345       [
42346         "Egypt (‫مصر‬‎)",
42347         "eg",
42348         "20"
42349       ],
42350       [
42351         "El Salvador",
42352         "sv",
42353         "503"
42354       ],
42355       [
42356         "Equatorial Guinea (Guinea Ecuatorial)",
42357         "gq",
42358         "240"
42359       ],
42360       [
42361         "Eritrea",
42362         "er",
42363         "291"
42364       ],
42365       [
42366         "Estonia (Eesti)",
42367         "ee",
42368         "372"
42369       ],
42370       [
42371         "Ethiopia",
42372         "et",
42373         "251"
42374       ],
42375       [
42376         "Falkland Islands (Islas Malvinas)",
42377         "fk",
42378         "500"
42379       ],
42380       [
42381         "Faroe Islands (Føroyar)",
42382         "fo",
42383         "298"
42384       ],
42385       [
42386         "Fiji",
42387         "fj",
42388         "679"
42389       ],
42390       [
42391         "Finland (Suomi)",
42392         "fi",
42393         "358",
42394         0
42395       ],
42396       [
42397         "France",
42398         "fr",
42399         "33"
42400       ],
42401       [
42402         "French Guiana (Guyane française)",
42403         "gf",
42404         "594"
42405       ],
42406       [
42407         "French Polynesia (Polynésie française)",
42408         "pf",
42409         "689"
42410       ],
42411       [
42412         "Gabon",
42413         "ga",
42414         "241"
42415       ],
42416       [
42417         "Gambia",
42418         "gm",
42419         "220"
42420       ],
42421       [
42422         "Georgia (საქართველო)",
42423         "ge",
42424         "995"
42425       ],
42426       [
42427         "Germany (Deutschland)",
42428         "de",
42429         "49"
42430       ],
42431       [
42432         "Ghana (Gaana)",
42433         "gh",
42434         "233"
42435       ],
42436       [
42437         "Gibraltar",
42438         "gi",
42439         "350"
42440       ],
42441       [
42442         "Greece (Ελλάδα)",
42443         "gr",
42444         "30"
42445       ],
42446       [
42447         "Greenland (Kalaallit Nunaat)",
42448         "gl",
42449         "299"
42450       ],
42451       [
42452         "Grenada",
42453         "gd",
42454         "1473"
42455       ],
42456       [
42457         "Guadeloupe",
42458         "gp",
42459         "590",
42460         0
42461       ],
42462       [
42463         "Guam",
42464         "gu",
42465         "1671"
42466       ],
42467       [
42468         "Guatemala",
42469         "gt",
42470         "502"
42471       ],
42472       [
42473         "Guernsey",
42474         "gg",
42475         "44",
42476         1
42477       ],
42478       [
42479         "Guinea (Guinée)",
42480         "gn",
42481         "224"
42482       ],
42483       [
42484         "Guinea-Bissau (Guiné Bissau)",
42485         "gw",
42486         "245"
42487       ],
42488       [
42489         "Guyana",
42490         "gy",
42491         "592"
42492       ],
42493       [
42494         "Haiti",
42495         "ht",
42496         "509"
42497       ],
42498       [
42499         "Honduras",
42500         "hn",
42501         "504"
42502       ],
42503       [
42504         "Hong Kong (香港)",
42505         "hk",
42506         "852"
42507       ],
42508       [
42509         "Hungary (Magyarország)",
42510         "hu",
42511         "36"
42512       ],
42513       [
42514         "Iceland (Ísland)",
42515         "is",
42516         "354"
42517       ],
42518       [
42519         "India (भारत)",
42520         "in",
42521         "91"
42522       ],
42523       [
42524         "Indonesia",
42525         "id",
42526         "62"
42527       ],
42528       [
42529         "Iran (‫ایران‬‎)",
42530         "ir",
42531         "98"
42532       ],
42533       [
42534         "Iraq (‫العراق‬‎)",
42535         "iq",
42536         "964"
42537       ],
42538       [
42539         "Ireland",
42540         "ie",
42541         "353"
42542       ],
42543       [
42544         "Isle of Man",
42545         "im",
42546         "44",
42547         2
42548       ],
42549       [
42550         "Israel (‫ישראל‬‎)",
42551         "il",
42552         "972"
42553       ],
42554       [
42555         "Italy (Italia)",
42556         "it",
42557         "39",
42558         0
42559       ],
42560       [
42561         "Jamaica",
42562         "jm",
42563         "1876"
42564       ],
42565       [
42566         "Japan (日本)",
42567         "jp",
42568         "81"
42569       ],
42570       [
42571         "Jersey",
42572         "je",
42573         "44",
42574         3
42575       ],
42576       [
42577         "Jordan (‫الأردن‬‎)",
42578         "jo",
42579         "962"
42580       ],
42581       [
42582         "Kazakhstan (Казахстан)",
42583         "kz",
42584         "7",
42585         1
42586       ],
42587       [
42588         "Kenya",
42589         "ke",
42590         "254"
42591       ],
42592       [
42593         "Kiribati",
42594         "ki",
42595         "686"
42596       ],
42597       [
42598         "Kosovo",
42599         "xk",
42600         "383"
42601       ],
42602       [
42603         "Kuwait (‫الكويت‬‎)",
42604         "kw",
42605         "965"
42606       ],
42607       [
42608         "Kyrgyzstan (Кыргызстан)",
42609         "kg",
42610         "996"
42611       ],
42612       [
42613         "Laos (ລາວ)",
42614         "la",
42615         "856"
42616       ],
42617       [
42618         "Latvia (Latvija)",
42619         "lv",
42620         "371"
42621       ],
42622       [
42623         "Lebanon (‫لبنان‬‎)",
42624         "lb",
42625         "961"
42626       ],
42627       [
42628         "Lesotho",
42629         "ls",
42630         "266"
42631       ],
42632       [
42633         "Liberia",
42634         "lr",
42635         "231"
42636       ],
42637       [
42638         "Libya (‫ليبيا‬‎)",
42639         "ly",
42640         "218"
42641       ],
42642       [
42643         "Liechtenstein",
42644         "li",
42645         "423"
42646       ],
42647       [
42648         "Lithuania (Lietuva)",
42649         "lt",
42650         "370"
42651       ],
42652       [
42653         "Luxembourg",
42654         "lu",
42655         "352"
42656       ],
42657       [
42658         "Macau (澳門)",
42659         "mo",
42660         "853"
42661       ],
42662       [
42663         "Macedonia (FYROM) (Македонија)",
42664         "mk",
42665         "389"
42666       ],
42667       [
42668         "Madagascar (Madagasikara)",
42669         "mg",
42670         "261"
42671       ],
42672       [
42673         "Malawi",
42674         "mw",
42675         "265"
42676       ],
42677       [
42678         "Malaysia",
42679         "my",
42680         "60"
42681       ],
42682       [
42683         "Maldives",
42684         "mv",
42685         "960"
42686       ],
42687       [
42688         "Mali",
42689         "ml",
42690         "223"
42691       ],
42692       [
42693         "Malta",
42694         "mt",
42695         "356"
42696       ],
42697       [
42698         "Marshall Islands",
42699         "mh",
42700         "692"
42701       ],
42702       [
42703         "Martinique",
42704         "mq",
42705         "596"
42706       ],
42707       [
42708         "Mauritania (‫موريتانيا‬‎)",
42709         "mr",
42710         "222"
42711       ],
42712       [
42713         "Mauritius (Moris)",
42714         "mu",
42715         "230"
42716       ],
42717       [
42718         "Mayotte",
42719         "yt",
42720         "262",
42721         1
42722       ],
42723       [
42724         "Mexico (México)",
42725         "mx",
42726         "52"
42727       ],
42728       [
42729         "Micronesia",
42730         "fm",
42731         "691"
42732       ],
42733       [
42734         "Moldova (Republica Moldova)",
42735         "md",
42736         "373"
42737       ],
42738       [
42739         "Monaco",
42740         "mc",
42741         "377"
42742       ],
42743       [
42744         "Mongolia (Монгол)",
42745         "mn",
42746         "976"
42747       ],
42748       [
42749         "Montenegro (Crna Gora)",
42750         "me",
42751         "382"
42752       ],
42753       [
42754         "Montserrat",
42755         "ms",
42756         "1664"
42757       ],
42758       [
42759         "Morocco (‫المغرب‬‎)",
42760         "ma",
42761         "212",
42762         0
42763       ],
42764       [
42765         "Mozambique (Moçambique)",
42766         "mz",
42767         "258"
42768       ],
42769       [
42770         "Myanmar (Burma) (မြန်မာ)",
42771         "mm",
42772         "95"
42773       ],
42774       [
42775         "Namibia (Namibië)",
42776         "na",
42777         "264"
42778       ],
42779       [
42780         "Nauru",
42781         "nr",
42782         "674"
42783       ],
42784       [
42785         "Nepal (नेपाल)",
42786         "np",
42787         "977"
42788       ],
42789       [
42790         "Netherlands (Nederland)",
42791         "nl",
42792         "31"
42793       ],
42794       [
42795         "New Caledonia (Nouvelle-Calédonie)",
42796         "nc",
42797         "687"
42798       ],
42799       [
42800         "New Zealand",
42801         "nz",
42802         "64"
42803       ],
42804       [
42805         "Nicaragua",
42806         "ni",
42807         "505"
42808       ],
42809       [
42810         "Niger (Nijar)",
42811         "ne",
42812         "227"
42813       ],
42814       [
42815         "Nigeria",
42816         "ng",
42817         "234"
42818       ],
42819       [
42820         "Niue",
42821         "nu",
42822         "683"
42823       ],
42824       [
42825         "Norfolk Island",
42826         "nf",
42827         "672"
42828       ],
42829       [
42830         "North Korea (조선 민주주의 인민 공화국)",
42831         "kp",
42832         "850"
42833       ],
42834       [
42835         "Northern Mariana Islands",
42836         "mp",
42837         "1670"
42838       ],
42839       [
42840         "Norway (Norge)",
42841         "no",
42842         "47",
42843         0
42844       ],
42845       [
42846         "Oman (‫عُمان‬‎)",
42847         "om",
42848         "968"
42849       ],
42850       [
42851         "Pakistan (‫پاکستان‬‎)",
42852         "pk",
42853         "92"
42854       ],
42855       [
42856         "Palau",
42857         "pw",
42858         "680"
42859       ],
42860       [
42861         "Palestine (‫فلسطين‬‎)",
42862         "ps",
42863         "970"
42864       ],
42865       [
42866         "Panama (Panamá)",
42867         "pa",
42868         "507"
42869       ],
42870       [
42871         "Papua New Guinea",
42872         "pg",
42873         "675"
42874       ],
42875       [
42876         "Paraguay",
42877         "py",
42878         "595"
42879       ],
42880       [
42881         "Peru (Perú)",
42882         "pe",
42883         "51"
42884       ],
42885       [
42886         "Philippines",
42887         "ph",
42888         "63"
42889       ],
42890       [
42891         "Poland (Polska)",
42892         "pl",
42893         "48"
42894       ],
42895       [
42896         "Portugal",
42897         "pt",
42898         "351"
42899       ],
42900       [
42901         "Puerto Rico",
42902         "pr",
42903         "1",
42904         3,
42905         ["787", "939"]
42906       ],
42907       [
42908         "Qatar (‫قطر‬‎)",
42909         "qa",
42910         "974"
42911       ],
42912       [
42913         "Réunion (La Réunion)",
42914         "re",
42915         "262",
42916         0
42917       ],
42918       [
42919         "Romania (România)",
42920         "ro",
42921         "40"
42922       ],
42923       [
42924         "Russia (Россия)",
42925         "ru",
42926         "7",
42927         0
42928       ],
42929       [
42930         "Rwanda",
42931         "rw",
42932         "250"
42933       ],
42934       [
42935         "Saint Barthélemy",
42936         "bl",
42937         "590",
42938         1
42939       ],
42940       [
42941         "Saint Helena",
42942         "sh",
42943         "290"
42944       ],
42945       [
42946         "Saint Kitts and Nevis",
42947         "kn",
42948         "1869"
42949       ],
42950       [
42951         "Saint Lucia",
42952         "lc",
42953         "1758"
42954       ],
42955       [
42956         "Saint Martin (Saint-Martin (partie française))",
42957         "mf",
42958         "590",
42959         2
42960       ],
42961       [
42962         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42963         "pm",
42964         "508"
42965       ],
42966       [
42967         "Saint Vincent and the Grenadines",
42968         "vc",
42969         "1784"
42970       ],
42971       [
42972         "Samoa",
42973         "ws",
42974         "685"
42975       ],
42976       [
42977         "San Marino",
42978         "sm",
42979         "378"
42980       ],
42981       [
42982         "São Tomé and Príncipe (São Tomé e Príncipe)",
42983         "st",
42984         "239"
42985       ],
42986       [
42987         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42988         "sa",
42989         "966"
42990       ],
42991       [
42992         "Senegal (Sénégal)",
42993         "sn",
42994         "221"
42995       ],
42996       [
42997         "Serbia (Србија)",
42998         "rs",
42999         "381"
43000       ],
43001       [
43002         "Seychelles",
43003         "sc",
43004         "248"
43005       ],
43006       [
43007         "Sierra Leone",
43008         "sl",
43009         "232"
43010       ],
43011       [
43012         "Singapore",
43013         "sg",
43014         "65"
43015       ],
43016       [
43017         "Sint Maarten",
43018         "sx",
43019         "1721"
43020       ],
43021       [
43022         "Slovakia (Slovensko)",
43023         "sk",
43024         "421"
43025       ],
43026       [
43027         "Slovenia (Slovenija)",
43028         "si",
43029         "386"
43030       ],
43031       [
43032         "Solomon Islands",
43033         "sb",
43034         "677"
43035       ],
43036       [
43037         "Somalia (Soomaaliya)",
43038         "so",
43039         "252"
43040       ],
43041       [
43042         "South Africa",
43043         "za",
43044         "27"
43045       ],
43046       [
43047         "South Korea (대한민국)",
43048         "kr",
43049         "82"
43050       ],
43051       [
43052         "South Sudan (‫جنوب السودان‬‎)",
43053         "ss",
43054         "211"
43055       ],
43056       [
43057         "Spain (España)",
43058         "es",
43059         "34"
43060       ],
43061       [
43062         "Sri Lanka (ශ්‍රී ලංකාව)",
43063         "lk",
43064         "94"
43065       ],
43066       [
43067         "Sudan (‫السودان‬‎)",
43068         "sd",
43069         "249"
43070       ],
43071       [
43072         "Suriname",
43073         "sr",
43074         "597"
43075       ],
43076       [
43077         "Svalbard and Jan Mayen",
43078         "sj",
43079         "47",
43080         1
43081       ],
43082       [
43083         "Swaziland",
43084         "sz",
43085         "268"
43086       ],
43087       [
43088         "Sweden (Sverige)",
43089         "se",
43090         "46"
43091       ],
43092       [
43093         "Switzerland (Schweiz)",
43094         "ch",
43095         "41"
43096       ],
43097       [
43098         "Syria (‫سوريا‬‎)",
43099         "sy",
43100         "963"
43101       ],
43102       [
43103         "Taiwan (台灣)",
43104         "tw",
43105         "886"
43106       ],
43107       [
43108         "Tajikistan",
43109         "tj",
43110         "992"
43111       ],
43112       [
43113         "Tanzania",
43114         "tz",
43115         "255"
43116       ],
43117       [
43118         "Thailand (ไทย)",
43119         "th",
43120         "66"
43121       ],
43122       [
43123         "Timor-Leste",
43124         "tl",
43125         "670"
43126       ],
43127       [
43128         "Togo",
43129         "tg",
43130         "228"
43131       ],
43132       [
43133         "Tokelau",
43134         "tk",
43135         "690"
43136       ],
43137       [
43138         "Tonga",
43139         "to",
43140         "676"
43141       ],
43142       [
43143         "Trinidad and Tobago",
43144         "tt",
43145         "1868"
43146       ],
43147       [
43148         "Tunisia (‫تونس‬‎)",
43149         "tn",
43150         "216"
43151       ],
43152       [
43153         "Turkey (Türkiye)",
43154         "tr",
43155         "90"
43156       ],
43157       [
43158         "Turkmenistan",
43159         "tm",
43160         "993"
43161       ],
43162       [
43163         "Turks and Caicos Islands",
43164         "tc",
43165         "1649"
43166       ],
43167       [
43168         "Tuvalu",
43169         "tv",
43170         "688"
43171       ],
43172       [
43173         "U.S. Virgin Islands",
43174         "vi",
43175         "1340"
43176       ],
43177       [
43178         "Uganda",
43179         "ug",
43180         "256"
43181       ],
43182       [
43183         "Ukraine (Україна)",
43184         "ua",
43185         "380"
43186       ],
43187       [
43188         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43189         "ae",
43190         "971"
43191       ],
43192       [
43193         "United Kingdom",
43194         "gb",
43195         "44",
43196         0
43197       ],
43198       [
43199         "United States",
43200         "us",
43201         "1",
43202         0
43203       ],
43204       [
43205         "Uruguay",
43206         "uy",
43207         "598"
43208       ],
43209       [
43210         "Uzbekistan (Oʻzbekiston)",
43211         "uz",
43212         "998"
43213       ],
43214       [
43215         "Vanuatu",
43216         "vu",
43217         "678"
43218       ],
43219       [
43220         "Vatican City (Città del Vaticano)",
43221         "va",
43222         "39",
43223         1
43224       ],
43225       [
43226         "Venezuela",
43227         "ve",
43228         "58"
43229       ],
43230       [
43231         "Vietnam (Việt Nam)",
43232         "vn",
43233         "84"
43234       ],
43235       [
43236         "Wallis and Futuna (Wallis-et-Futuna)",
43237         "wf",
43238         "681"
43239       ],
43240       [
43241         "Western Sahara (‫الصحراء الغربية‬‎)",
43242         "eh",
43243         "212",
43244         1
43245       ],
43246       [
43247         "Yemen (‫اليمن‬‎)",
43248         "ye",
43249         "967"
43250       ],
43251       [
43252         "Zambia",
43253         "zm",
43254         "260"
43255       ],
43256       [
43257         "Zimbabwe",
43258         "zw",
43259         "263"
43260       ],
43261       [
43262         "Åland Islands",
43263         "ax",
43264         "358",
43265         1
43266       ]
43267   ];
43268   
43269   return d;
43270 }/**
43271 *    This script refer to:
43272 *    Title: International Telephone Input
43273 *    Author: Jack O'Connor
43274 *    Code version:  v12.1.12
43275 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43276 **/
43277
43278 /**
43279  * @class Roo.bootstrap.PhoneInput
43280  * @extends Roo.bootstrap.TriggerField
43281  * An input with International dial-code selection
43282  
43283  * @cfg {String} defaultDialCode default '+852'
43284  * @cfg {Array} preferedCountries default []
43285   
43286  * @constructor
43287  * Create a new PhoneInput.
43288  * @param {Object} config Configuration options
43289  */
43290
43291 Roo.bootstrap.PhoneInput = function(config) {
43292     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43293 };
43294
43295 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43296         /**
43297         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43298         */
43299         listWidth: undefined,
43300         
43301         selectedClass: 'active',
43302         
43303         invalidClass : "has-warning",
43304         
43305         validClass: 'has-success',
43306         
43307         allowed: '0123456789',
43308         
43309         max_length: 15,
43310         
43311         /**
43312          * @cfg {String} defaultDialCode The default dial code when initializing the input
43313          */
43314         defaultDialCode: '+852',
43315         
43316         /**
43317          * @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
43318          */
43319         preferedCountries: false,
43320         
43321         getAutoCreate : function()
43322         {
43323             var data = Roo.bootstrap.PhoneInputData();
43324             var align = this.labelAlign || this.parentLabelAlign();
43325             var id = Roo.id();
43326             
43327             this.allCountries = [];
43328             this.dialCodeMapping = [];
43329             
43330             for (var i = 0; i < data.length; i++) {
43331               var c = data[i];
43332               this.allCountries[i] = {
43333                 name: c[0],
43334                 iso2: c[1],
43335                 dialCode: c[2],
43336                 priority: c[3] || 0,
43337                 areaCodes: c[4] || null
43338               };
43339               this.dialCodeMapping[c[2]] = {
43340                   name: c[0],
43341                   iso2: c[1],
43342                   priority: c[3] || 0,
43343                   areaCodes: c[4] || null
43344               };
43345             }
43346             
43347             var cfg = {
43348                 cls: 'form-group',
43349                 cn: []
43350             };
43351             
43352             var input =  {
43353                 tag: 'input',
43354                 id : id,
43355                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43356                 maxlength: this.max_length,
43357                 cls : 'form-control tel-input',
43358                 autocomplete: 'new-password'
43359             };
43360             
43361             var hiddenInput = {
43362                 tag: 'input',
43363                 type: 'hidden',
43364                 cls: 'hidden-tel-input'
43365             };
43366             
43367             if (this.name) {
43368                 hiddenInput.name = this.name;
43369             }
43370             
43371             if (this.disabled) {
43372                 input.disabled = true;
43373             }
43374             
43375             var flag_container = {
43376                 tag: 'div',
43377                 cls: 'flag-box',
43378                 cn: [
43379                     {
43380                         tag: 'div',
43381                         cls: 'flag'
43382                     },
43383                     {
43384                         tag: 'div',
43385                         cls: 'caret'
43386                     }
43387                 ]
43388             };
43389             
43390             var box = {
43391                 tag: 'div',
43392                 cls: this.hasFeedback ? 'has-feedback' : '',
43393                 cn: [
43394                     hiddenInput,
43395                     input,
43396                     {
43397                         tag: 'input',
43398                         cls: 'dial-code-holder',
43399                         disabled: true
43400                     }
43401                 ]
43402             };
43403             
43404             var container = {
43405                 cls: 'roo-select2-container input-group',
43406                 cn: [
43407                     flag_container,
43408                     box
43409                 ]
43410             };
43411             
43412             if (this.fieldLabel.length) {
43413                 var indicator = {
43414                     tag: 'i',
43415                     tooltip: 'This field is required'
43416                 };
43417                 
43418                 var label = {
43419                     tag: 'label',
43420                     'for':  id,
43421                     cls: 'control-label',
43422                     cn: []
43423                 };
43424                 
43425                 var label_text = {
43426                     tag: 'span',
43427                     html: this.fieldLabel
43428                 };
43429                 
43430                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43431                 label.cn = [
43432                     indicator,
43433                     label_text
43434                 ];
43435                 
43436                 if(this.indicatorpos == 'right') {
43437                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43438                     label.cn = [
43439                         label_text,
43440                         indicator
43441                     ];
43442                 }
43443                 
43444                 if(align == 'left') {
43445                     container = {
43446                         tag: 'div',
43447                         cn: [
43448                             container
43449                         ]
43450                     };
43451                     
43452                     if(this.labelWidth > 12){
43453                         label.style = "width: " + this.labelWidth + 'px';
43454                     }
43455                     if(this.labelWidth < 13 && this.labelmd == 0){
43456                         this.labelmd = this.labelWidth;
43457                     }
43458                     if(this.labellg > 0){
43459                         label.cls += ' col-lg-' + this.labellg;
43460                         input.cls += ' col-lg-' + (12 - this.labellg);
43461                     }
43462                     if(this.labelmd > 0){
43463                         label.cls += ' col-md-' + this.labelmd;
43464                         container.cls += ' col-md-' + (12 - this.labelmd);
43465                     }
43466                     if(this.labelsm > 0){
43467                         label.cls += ' col-sm-' + this.labelsm;
43468                         container.cls += ' col-sm-' + (12 - this.labelsm);
43469                     }
43470                     if(this.labelxs > 0){
43471                         label.cls += ' col-xs-' + this.labelxs;
43472                         container.cls += ' col-xs-' + (12 - this.labelxs);
43473                     }
43474                 }
43475             }
43476             
43477             cfg.cn = [
43478                 label,
43479                 container
43480             ];
43481             
43482             var settings = this;
43483             
43484             ['xs','sm','md','lg'].map(function(size){
43485                 if (settings[size]) {
43486                     cfg.cls += ' col-' + size + '-' + settings[size];
43487                 }
43488             });
43489             
43490             this.store = new Roo.data.Store({
43491                 proxy : new Roo.data.MemoryProxy({}),
43492                 reader : new Roo.data.JsonReader({
43493                     fields : [
43494                         {
43495                             'name' : 'name',
43496                             'type' : 'string'
43497                         },
43498                         {
43499                             'name' : 'iso2',
43500                             'type' : 'string'
43501                         },
43502                         {
43503                             'name' : 'dialCode',
43504                             'type' : 'string'
43505                         },
43506                         {
43507                             'name' : 'priority',
43508                             'type' : 'string'
43509                         },
43510                         {
43511                             'name' : 'areaCodes',
43512                             'type' : 'string'
43513                         }
43514                     ]
43515                 })
43516             });
43517             
43518             if(!this.preferedCountries) {
43519                 this.preferedCountries = [
43520                     'hk',
43521                     'gb',
43522                     'us'
43523                 ];
43524             }
43525             
43526             var p = this.preferedCountries.reverse();
43527             
43528             if(p) {
43529                 for (var i = 0; i < p.length; i++) {
43530                     for (var j = 0; j < this.allCountries.length; j++) {
43531                         if(this.allCountries[j].iso2 == p[i]) {
43532                             var t = this.allCountries[j];
43533                             this.allCountries.splice(j,1);
43534                             this.allCountries.unshift(t);
43535                         }
43536                     } 
43537                 }
43538             }
43539             
43540             this.store.proxy.data = {
43541                 success: true,
43542                 data: this.allCountries
43543             };
43544             
43545             return cfg;
43546         },
43547         
43548         initEvents : function()
43549         {
43550             this.createList();
43551             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43552             
43553             this.indicator = this.indicatorEl();
43554             this.flag = this.flagEl();
43555             this.dialCodeHolder = this.dialCodeHolderEl();
43556             
43557             this.trigger = this.el.select('div.flag-box',true).first();
43558             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43559             
43560             var _this = this;
43561             
43562             (function(){
43563                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43564                 _this.list.setWidth(lw);
43565             }).defer(100);
43566             
43567             this.list.on('mouseover', this.onViewOver, this);
43568             this.list.on('mousemove', this.onViewMove, this);
43569             this.inputEl().on("keyup", this.onKeyUp, this);
43570             this.inputEl().on("keypress", this.onKeyPress, this);
43571             
43572             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43573
43574             this.view = new Roo.View(this.list, this.tpl, {
43575                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43576             });
43577             
43578             this.view.on('click', this.onViewClick, this);
43579             this.setValue(this.defaultDialCode);
43580         },
43581         
43582         onTriggerClick : function(e)
43583         {
43584             Roo.log('trigger click');
43585             if(this.disabled){
43586                 return;
43587             }
43588             
43589             if(this.isExpanded()){
43590                 this.collapse();
43591                 this.hasFocus = false;
43592             }else {
43593                 this.store.load({});
43594                 this.hasFocus = true;
43595                 this.expand();
43596             }
43597         },
43598         
43599         isExpanded : function()
43600         {
43601             return this.list.isVisible();
43602         },
43603         
43604         collapse : function()
43605         {
43606             if(!this.isExpanded()){
43607                 return;
43608             }
43609             this.list.hide();
43610             Roo.get(document).un('mousedown', this.collapseIf, this);
43611             Roo.get(document).un('mousewheel', this.collapseIf, this);
43612             this.fireEvent('collapse', this);
43613             this.validate();
43614         },
43615         
43616         expand : function()
43617         {
43618             Roo.log('expand');
43619
43620             if(this.isExpanded() || !this.hasFocus){
43621                 return;
43622             }
43623             
43624             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43625             this.list.setWidth(lw);
43626             
43627             this.list.show();
43628             this.restrictHeight();
43629             
43630             Roo.get(document).on('mousedown', this.collapseIf, this);
43631             Roo.get(document).on('mousewheel', this.collapseIf, this);
43632             
43633             this.fireEvent('expand', this);
43634         },
43635         
43636         restrictHeight : function()
43637         {
43638             this.list.alignTo(this.inputEl(), this.listAlign);
43639             this.list.alignTo(this.inputEl(), this.listAlign);
43640         },
43641         
43642         onViewOver : function(e, t)
43643         {
43644             if(this.inKeyMode){
43645                 return;
43646             }
43647             var item = this.view.findItemFromChild(t);
43648             
43649             if(item){
43650                 var index = this.view.indexOf(item);
43651                 this.select(index, false);
43652             }
43653         },
43654
43655         // private
43656         onViewClick : function(view, doFocus, el, e)
43657         {
43658             var index = this.view.getSelectedIndexes()[0];
43659             
43660             var r = this.store.getAt(index);
43661             
43662             if(r){
43663                 this.onSelect(r, index);
43664             }
43665             if(doFocus !== false && !this.blockFocus){
43666                 this.inputEl().focus();
43667             }
43668         },
43669         
43670         onViewMove : function(e, t)
43671         {
43672             this.inKeyMode = false;
43673         },
43674         
43675         select : function(index, scrollIntoView)
43676         {
43677             this.selectedIndex = index;
43678             this.view.select(index);
43679             if(scrollIntoView !== false){
43680                 var el = this.view.getNode(index);
43681                 if(el){
43682                     this.list.scrollChildIntoView(el, false);
43683                 }
43684             }
43685         },
43686         
43687         createList : function()
43688         {
43689             this.list = Roo.get(document.body).createChild({
43690                 tag: 'ul',
43691                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43692                 style: 'display:none'
43693             });
43694             
43695             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43696         },
43697         
43698         collapseIf : function(e)
43699         {
43700             var in_combo  = e.within(this.el);
43701             var in_list =  e.within(this.list);
43702             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43703             
43704             if (in_combo || in_list || is_list) {
43705                 return;
43706             }
43707             this.collapse();
43708         },
43709         
43710         onSelect : function(record, index)
43711         {
43712             if(this.fireEvent('beforeselect', this, record, index) !== false){
43713                 
43714                 this.setFlagClass(record.data.iso2);
43715                 this.setDialCode(record.data.dialCode);
43716                 this.hasFocus = false;
43717                 this.collapse();
43718                 this.fireEvent('select', this, record, index);
43719             }
43720         },
43721         
43722         flagEl : function()
43723         {
43724             var flag = this.el.select('div.flag',true).first();
43725             if(!flag){
43726                 return false;
43727             }
43728             return flag;
43729         },
43730         
43731         dialCodeHolderEl : function()
43732         {
43733             var d = this.el.select('input.dial-code-holder',true).first();
43734             if(!d){
43735                 return false;
43736             }
43737             return d;
43738         },
43739         
43740         setDialCode : function(v)
43741         {
43742             this.dialCodeHolder.dom.value = '+'+v;
43743         },
43744         
43745         setFlagClass : function(n)
43746         {
43747             this.flag.dom.className = 'flag '+n;
43748         },
43749         
43750         getValue : function()
43751         {
43752             var v = this.inputEl().getValue();
43753             if(this.dialCodeHolder) {
43754                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43755             }
43756             return v;
43757         },
43758         
43759         setValue : function(v)
43760         {
43761             var d = this.getDialCode(v);
43762             
43763             //invalid dial code
43764             if(v.length == 0 || !d || d.length == 0) {
43765                 if(this.rendered){
43766                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43767                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43768                 }
43769                 return;
43770             }
43771             
43772             //valid dial code
43773             this.setFlagClass(this.dialCodeMapping[d].iso2);
43774             this.setDialCode(d);
43775             this.inputEl().dom.value = v.replace('+'+d,'');
43776             this.hiddenEl().dom.value = this.getValue();
43777             
43778             this.validate();
43779         },
43780         
43781         getDialCode : function(v)
43782         {
43783             v = v ||  '';
43784             
43785             if (v.length == 0) {
43786                 return this.dialCodeHolder.dom.value;
43787             }
43788             
43789             var dialCode = "";
43790             if (v.charAt(0) != "+") {
43791                 return false;
43792             }
43793             var numericChars = "";
43794             for (var i = 1; i < v.length; i++) {
43795               var c = v.charAt(i);
43796               if (!isNaN(c)) {
43797                 numericChars += c;
43798                 if (this.dialCodeMapping[numericChars]) {
43799                   dialCode = v.substr(1, i);
43800                 }
43801                 if (numericChars.length == 4) {
43802                   break;
43803                 }
43804               }
43805             }
43806             return dialCode;
43807         },
43808         
43809         reset : function()
43810         {
43811             this.setValue(this.defaultDialCode);
43812             this.validate();
43813         },
43814         
43815         hiddenEl : function()
43816         {
43817             return this.el.select('input.hidden-tel-input',true).first();
43818         },
43819         
43820         // after setting val
43821         onKeyUp : function(e){
43822             this.setValue(this.getValue());
43823         },
43824         
43825         onKeyPress : function(e){
43826             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43827                 e.stopEvent();
43828             }
43829         }
43830         
43831 });
43832 /**
43833  * @class Roo.bootstrap.MoneyField
43834  * @extends Roo.bootstrap.ComboBox
43835  * Bootstrap MoneyField class
43836  * 
43837  * @constructor
43838  * Create a new MoneyField.
43839  * @param {Object} config Configuration options
43840  */
43841
43842 Roo.bootstrap.MoneyField = function(config) {
43843     
43844     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43845     
43846 };
43847
43848 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43849     
43850     /**
43851      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43852      */
43853     allowDecimals : true,
43854     /**
43855      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43856      */
43857     decimalSeparator : ".",
43858     /**
43859      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43860      */
43861     decimalPrecision : 0,
43862     /**
43863      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43864      */
43865     allowNegative : true,
43866     /**
43867      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43868      */
43869     allowZero: true,
43870     /**
43871      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43872      */
43873     minValue : Number.NEGATIVE_INFINITY,
43874     /**
43875      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43876      */
43877     maxValue : Number.MAX_VALUE,
43878     /**
43879      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43880      */
43881     minText : "The minimum value for this field is {0}",
43882     /**
43883      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43884      */
43885     maxText : "The maximum value for this field is {0}",
43886     /**
43887      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43888      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43889      */
43890     nanText : "{0} is not a valid number",
43891     /**
43892      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43893      */
43894     castInt : true,
43895     /**
43896      * @cfg {String} defaults currency of the MoneyField
43897      * value should be in lkey
43898      */
43899     defaultCurrency : false,
43900     /**
43901      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43902      */
43903     thousandsDelimiter : false,
43904     /**
43905      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43906      */
43907     max_length: false,
43908     
43909     inputlg : 9,
43910     inputmd : 9,
43911     inputsm : 9,
43912     inputxs : 6,
43913     
43914     store : false,
43915     
43916     getAutoCreate : function()
43917     {
43918         var align = this.labelAlign || this.parentLabelAlign();
43919         
43920         var id = Roo.id();
43921
43922         var cfg = {
43923             cls: 'form-group',
43924             cn: []
43925         };
43926
43927         var input =  {
43928             tag: 'input',
43929             id : id,
43930             cls : 'form-control roo-money-amount-input',
43931             autocomplete: 'new-password'
43932         };
43933         
43934         var hiddenInput = {
43935             tag: 'input',
43936             type: 'hidden',
43937             id: Roo.id(),
43938             cls: 'hidden-number-input'
43939         };
43940         
43941         if(this.max_length) {
43942             input.maxlength = this.max_length; 
43943         }
43944         
43945         if (this.name) {
43946             hiddenInput.name = this.name;
43947         }
43948
43949         if (this.disabled) {
43950             input.disabled = true;
43951         }
43952
43953         var clg = 12 - this.inputlg;
43954         var cmd = 12 - this.inputmd;
43955         var csm = 12 - this.inputsm;
43956         var cxs = 12 - this.inputxs;
43957         
43958         var container = {
43959             tag : 'div',
43960             cls : 'row roo-money-field',
43961             cn : [
43962                 {
43963                     tag : 'div',
43964                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43965                     cn : [
43966                         {
43967                             tag : 'div',
43968                             cls: 'roo-select2-container input-group',
43969                             cn: [
43970                                 {
43971                                     tag : 'input',
43972                                     cls : 'form-control roo-money-currency-input',
43973                                     autocomplete: 'new-password',
43974                                     readOnly : 1,
43975                                     name : this.currencyName
43976                                 },
43977                                 {
43978                                     tag :'span',
43979                                     cls : 'input-group-addon',
43980                                     cn : [
43981                                         {
43982                                             tag: 'span',
43983                                             cls: 'caret'
43984                                         }
43985                                     ]
43986                                 }
43987                             ]
43988                         }
43989                     ]
43990                 },
43991                 {
43992                     tag : 'div',
43993                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43994                     cn : [
43995                         {
43996                             tag: 'div',
43997                             cls: this.hasFeedback ? 'has-feedback' : '',
43998                             cn: [
43999                                 input
44000                             ]
44001                         }
44002                     ]
44003                 }
44004             ]
44005             
44006         };
44007         
44008         if (this.fieldLabel.length) {
44009             var indicator = {
44010                 tag: 'i',
44011                 tooltip: 'This field is required'
44012             };
44013
44014             var label = {
44015                 tag: 'label',
44016                 'for':  id,
44017                 cls: 'control-label',
44018                 cn: []
44019             };
44020
44021             var label_text = {
44022                 tag: 'span',
44023                 html: this.fieldLabel
44024             };
44025
44026             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44027             label.cn = [
44028                 indicator,
44029                 label_text
44030             ];
44031
44032             if(this.indicatorpos == 'right') {
44033                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44034                 label.cn = [
44035                     label_text,
44036                     indicator
44037                 ];
44038             }
44039
44040             if(align == 'left') {
44041                 container = {
44042                     tag: 'div',
44043                     cn: [
44044                         container
44045                     ]
44046                 };
44047
44048                 if(this.labelWidth > 12){
44049                     label.style = "width: " + this.labelWidth + 'px';
44050                 }
44051                 if(this.labelWidth < 13 && this.labelmd == 0){
44052                     this.labelmd = this.labelWidth;
44053                 }
44054                 if(this.labellg > 0){
44055                     label.cls += ' col-lg-' + this.labellg;
44056                     input.cls += ' col-lg-' + (12 - this.labellg);
44057                 }
44058                 if(this.labelmd > 0){
44059                     label.cls += ' col-md-' + this.labelmd;
44060                     container.cls += ' col-md-' + (12 - this.labelmd);
44061                 }
44062                 if(this.labelsm > 0){
44063                     label.cls += ' col-sm-' + this.labelsm;
44064                     container.cls += ' col-sm-' + (12 - this.labelsm);
44065                 }
44066                 if(this.labelxs > 0){
44067                     label.cls += ' col-xs-' + this.labelxs;
44068                     container.cls += ' col-xs-' + (12 - this.labelxs);
44069                 }
44070             }
44071         }
44072
44073         cfg.cn = [
44074             label,
44075             container,
44076             hiddenInput
44077         ];
44078         
44079         var settings = this;
44080
44081         ['xs','sm','md','lg'].map(function(size){
44082             if (settings[size]) {
44083                 cfg.cls += ' col-' + size + '-' + settings[size];
44084             }
44085         });
44086         
44087         return cfg;
44088     },
44089     
44090     initEvents : function()
44091     {
44092         this.indicator = this.indicatorEl();
44093         
44094         this.initCurrencyEvent();
44095         
44096         this.initNumberEvent();
44097     },
44098     
44099     initCurrencyEvent : function()
44100     {
44101         if (!this.store) {
44102             throw "can not find store for combo";
44103         }
44104         
44105         this.store = Roo.factory(this.store, Roo.data);
44106         this.store.parent = this;
44107         
44108         this.createList();
44109         
44110         this.triggerEl = this.el.select('.input-group-addon', true).first();
44111         
44112         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44113         
44114         var _this = this;
44115         
44116         (function(){
44117             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44118             _this.list.setWidth(lw);
44119         }).defer(100);
44120         
44121         this.list.on('mouseover', this.onViewOver, this);
44122         this.list.on('mousemove', this.onViewMove, this);
44123         this.list.on('scroll', this.onViewScroll, this);
44124         
44125         if(!this.tpl){
44126             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44127         }
44128         
44129         this.view = new Roo.View(this.list, this.tpl, {
44130             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44131         });
44132         
44133         this.view.on('click', this.onViewClick, this);
44134         
44135         this.store.on('beforeload', this.onBeforeLoad, this);
44136         this.store.on('load', this.onLoad, this);
44137         this.store.on('loadexception', this.onLoadException, this);
44138         
44139         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44140             "up" : function(e){
44141                 this.inKeyMode = true;
44142                 this.selectPrev();
44143             },
44144
44145             "down" : function(e){
44146                 if(!this.isExpanded()){
44147                     this.onTriggerClick();
44148                 }else{
44149                     this.inKeyMode = true;
44150                     this.selectNext();
44151                 }
44152             },
44153
44154             "enter" : function(e){
44155                 this.collapse();
44156                 
44157                 if(this.fireEvent("specialkey", this, e)){
44158                     this.onViewClick(false);
44159                 }
44160                 
44161                 return true;
44162             },
44163
44164             "esc" : function(e){
44165                 this.collapse();
44166             },
44167
44168             "tab" : function(e){
44169                 this.collapse();
44170                 
44171                 if(this.fireEvent("specialkey", this, e)){
44172                     this.onViewClick(false);
44173                 }
44174                 
44175                 return true;
44176             },
44177
44178             scope : this,
44179
44180             doRelay : function(foo, bar, hname){
44181                 if(hname == 'down' || this.scope.isExpanded()){
44182                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44183                 }
44184                 return true;
44185             },
44186
44187             forceKeyDown: true
44188         });
44189         
44190         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44191         
44192     },
44193     
44194     initNumberEvent : function(e)
44195     {
44196         this.inputEl().on("keydown" , this.fireKey,  this);
44197         this.inputEl().on("focus", this.onFocus,  this);
44198         this.inputEl().on("blur", this.onBlur,  this);
44199         
44200         this.inputEl().relayEvent('keyup', this);
44201         
44202         if(this.indicator){
44203             this.indicator.addClass('invisible');
44204         }
44205  
44206         this.originalValue = this.getValue();
44207         
44208         if(this.validationEvent == 'keyup'){
44209             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44210             this.inputEl().on('keyup', this.filterValidation, this);
44211         }
44212         else if(this.validationEvent !== false){
44213             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44214         }
44215         
44216         if(this.selectOnFocus){
44217             this.on("focus", this.preFocus, this);
44218             
44219         }
44220         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44221             this.inputEl().on("keypress", this.filterKeys, this);
44222         } else {
44223             this.inputEl().relayEvent('keypress', this);
44224         }
44225         
44226         var allowed = "0123456789";
44227         
44228         if(this.allowDecimals){
44229             allowed += this.decimalSeparator;
44230         }
44231         
44232         if(this.allowNegative){
44233             allowed += "-";
44234         }
44235         
44236         if(this.thousandsDelimiter) {
44237             allowed += ",";
44238         }
44239         
44240         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44241         
44242         var keyPress = function(e){
44243             
44244             var k = e.getKey();
44245             
44246             var c = e.getCharCode();
44247             
44248             if(
44249                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44250                     allowed.indexOf(String.fromCharCode(c)) === -1
44251             ){
44252                 e.stopEvent();
44253                 return;
44254             }
44255             
44256             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44257                 return;
44258             }
44259             
44260             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44261                 e.stopEvent();
44262             }
44263         };
44264         
44265         this.inputEl().on("keypress", keyPress, this);
44266         
44267     },
44268     
44269     onTriggerClick : function(e)
44270     {   
44271         if(this.disabled){
44272             return;
44273         }
44274         
44275         this.page = 0;
44276         this.loadNext = false;
44277         
44278         if(this.isExpanded()){
44279             this.collapse();
44280             return;
44281         }
44282         
44283         this.hasFocus = true;
44284         
44285         if(this.triggerAction == 'all') {
44286             this.doQuery(this.allQuery, true);
44287             return;
44288         }
44289         
44290         this.doQuery(this.getRawValue());
44291     },
44292     
44293     getCurrency : function()
44294     {   
44295         var v = this.currencyEl().getValue();
44296         
44297         return v;
44298     },
44299     
44300     restrictHeight : function()
44301     {
44302         this.list.alignTo(this.currencyEl(), this.listAlign);
44303         this.list.alignTo(this.currencyEl(), this.listAlign);
44304     },
44305     
44306     onViewClick : function(view, doFocus, el, e)
44307     {
44308         var index = this.view.getSelectedIndexes()[0];
44309         
44310         var r = this.store.getAt(index);
44311         
44312         if(r){
44313             this.onSelect(r, index);
44314         }
44315     },
44316     
44317     onSelect : function(record, index){
44318         
44319         if(this.fireEvent('beforeselect', this, record, index) !== false){
44320         
44321             this.setFromCurrencyData(index > -1 ? record.data : false);
44322             
44323             this.collapse();
44324             
44325             this.fireEvent('select', this, record, index);
44326         }
44327     },
44328     
44329     setFromCurrencyData : function(o)
44330     {
44331         var currency = '';
44332         
44333         this.lastCurrency = o;
44334         
44335         if (this.currencyField) {
44336             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44337         } else {
44338             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44339         }
44340         
44341         this.lastSelectionText = currency;
44342         
44343         //setting default currency
44344         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44345             this.setCurrency(this.defaultCurrency);
44346             return;
44347         }
44348         
44349         this.setCurrency(currency);
44350     },
44351     
44352     setFromData : function(o)
44353     {
44354         var c = {};
44355         
44356         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44357         
44358         this.setFromCurrencyData(c);
44359         
44360         var value = '';
44361         
44362         if (this.name) {
44363             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44364         } else {
44365             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44366         }
44367         
44368         this.setValue(value);
44369         
44370     },
44371     
44372     setCurrency : function(v)
44373     {   
44374         this.currencyValue = v;
44375         
44376         if(this.rendered){
44377             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44378             this.validate();
44379         }
44380     },
44381     
44382     setValue : function(v)
44383     {
44384         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44385         
44386         this.value = v;
44387         
44388         if(this.rendered){
44389             
44390             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44391             
44392             this.inputEl().dom.value = (v == '') ? '' :
44393                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44394             
44395             if(!this.allowZero && v === '0') {
44396                 this.hiddenEl().dom.value = '';
44397                 this.inputEl().dom.value = '';
44398             }
44399             
44400             this.validate();
44401         }
44402     },
44403     
44404     getRawValue : function()
44405     {
44406         var v = this.inputEl().getValue();
44407         
44408         return v;
44409     },
44410     
44411     getValue : function()
44412     {
44413         return this.fixPrecision(this.parseValue(this.getRawValue()));
44414     },
44415     
44416     parseValue : function(value)
44417     {
44418         if(this.thousandsDelimiter) {
44419             value += "";
44420             r = new RegExp(",", "g");
44421             value = value.replace(r, "");
44422         }
44423         
44424         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44425         return isNaN(value) ? '' : value;
44426         
44427     },
44428     
44429     fixPrecision : function(value)
44430     {
44431         if(this.thousandsDelimiter) {
44432             value += "";
44433             r = new RegExp(",", "g");
44434             value = value.replace(r, "");
44435         }
44436         
44437         var nan = isNaN(value);
44438         
44439         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44440             return nan ? '' : value;
44441         }
44442         return parseFloat(value).toFixed(this.decimalPrecision);
44443     },
44444     
44445     decimalPrecisionFcn : function(v)
44446     {
44447         return Math.floor(v);
44448     },
44449     
44450     validateValue : function(value)
44451     {
44452         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44453             return false;
44454         }
44455         
44456         var num = this.parseValue(value);
44457         
44458         if(isNaN(num)){
44459             this.markInvalid(String.format(this.nanText, value));
44460             return false;
44461         }
44462         
44463         if(num < this.minValue){
44464             this.markInvalid(String.format(this.minText, this.minValue));
44465             return false;
44466         }
44467         
44468         if(num > this.maxValue){
44469             this.markInvalid(String.format(this.maxText, this.maxValue));
44470             return false;
44471         }
44472         
44473         return true;
44474     },
44475     
44476     validate : function()
44477     {
44478         if(this.disabled || this.allowBlank){
44479             this.markValid();
44480             return true;
44481         }
44482         
44483         var currency = this.getCurrency();
44484         
44485         if(this.validateValue(this.getRawValue()) && currency.length){
44486             this.markValid();
44487             return true;
44488         }
44489         
44490         this.markInvalid();
44491         return false;
44492     },
44493     
44494     getName: function()
44495     {
44496         return this.name;
44497     },
44498     
44499     beforeBlur : function()
44500     {
44501         if(!this.castInt){
44502             return;
44503         }
44504         
44505         var v = this.parseValue(this.getRawValue());
44506         
44507         if(v || v == 0){
44508             this.setValue(v);
44509         }
44510     },
44511     
44512     onBlur : function()
44513     {
44514         this.beforeBlur();
44515         
44516         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44517             //this.el.removeClass(this.focusClass);
44518         }
44519         
44520         this.hasFocus = false;
44521         
44522         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44523             this.validate();
44524         }
44525         
44526         var v = this.getValue();
44527         
44528         if(String(v) !== String(this.startValue)){
44529             this.fireEvent('change', this, v, this.startValue);
44530         }
44531         
44532         this.fireEvent("blur", this);
44533     },
44534     
44535     inputEl : function()
44536     {
44537         return this.el.select('.roo-money-amount-input', true).first();
44538     },
44539     
44540     currencyEl : function()
44541     {
44542         return this.el.select('.roo-money-currency-input', true).first();
44543     },
44544     
44545     hiddenEl : function()
44546     {
44547         return this.el.select('input.hidden-number-input',true).first();
44548     }
44549     
44550 });/**
44551  * @class Roo.bootstrap.BezierSignature
44552  * @extends Roo.bootstrap.Component
44553  * Bootstrap BezierSignature class
44554  * This script refer to:
44555  *    Title: Signature Pad
44556  *    Author: szimek
44557  *    Availability: https://github.com/szimek/signature_pad
44558  *
44559  * @constructor
44560  * Create a new BezierSignature
44561  * @param {Object} config The config object
44562  */
44563
44564 Roo.bootstrap.BezierSignature = function(config){
44565     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44566     this.addEvents({
44567         "resize" : true
44568     });
44569 };
44570
44571 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44572 {
44573      
44574     curve_data: [],
44575     
44576     is_empty: true,
44577     
44578     mouse_btn_down: true,
44579     
44580     /**
44581      * @cfg {int} canvas height
44582      */
44583     canvas_height: '200px',
44584     
44585     /**
44586      * @cfg {float|function} Radius of a single dot.
44587      */ 
44588     dot_size: false,
44589     
44590     /**
44591      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44592      */
44593     min_width: 0.5,
44594     
44595     /**
44596      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44597      */
44598     max_width: 2.5,
44599     
44600     /**
44601      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44602      */
44603     throttle: 16,
44604     
44605     /**
44606      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44607      */
44608     min_distance: 5,
44609     
44610     /**
44611      * @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.
44612      */
44613     bg_color: 'rgba(0, 0, 0, 0)',
44614     
44615     /**
44616      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44617      */
44618     dot_color: 'black',
44619     
44620     /**
44621      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44622      */ 
44623     velocity_filter_weight: 0.7,
44624     
44625     /**
44626      * @cfg {function} Callback when stroke begin. 
44627      */
44628     onBegin: false,
44629     
44630     /**
44631      * @cfg {function} Callback when stroke end.
44632      */
44633     onEnd: false,
44634     
44635     getAutoCreate : function()
44636     {
44637         var cls = 'roo-signature column';
44638         
44639         if(this.cls){
44640             cls += ' ' + this.cls;
44641         }
44642         
44643         var col_sizes = [
44644             'lg',
44645             'md',
44646             'sm',
44647             'xs'
44648         ];
44649         
44650         for(var i = 0; i < col_sizes.length; i++) {
44651             if(this[col_sizes[i]]) {
44652                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44653             }
44654         }
44655         
44656         var cfg = {
44657             tag: 'div',
44658             cls: cls,
44659             cn: [
44660                 {
44661                     tag: 'div',
44662                     cls: 'roo-signature-body',
44663                     cn: [
44664                         {
44665                             tag: 'canvas',
44666                             cls: 'roo-signature-body-canvas',
44667                             height: this.canvas_height,
44668                             width: this.canvas_width
44669                         }
44670                     ]
44671                 },
44672                 {
44673                     tag: 'input',
44674                     type: 'file',
44675                     style: 'display: none'
44676                 }
44677             ]
44678         };
44679         
44680         return cfg;
44681     },
44682     
44683     initEvents: function() 
44684     {
44685         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44686         
44687         var canvas = this.canvasEl();
44688         
44689         // mouse && touch event swapping...
44690         canvas.dom.style.touchAction = 'none';
44691         canvas.dom.style.msTouchAction = 'none';
44692         
44693         this.mouse_btn_down = false;
44694         canvas.on('mousedown', this._handleMouseDown, this);
44695         canvas.on('mousemove', this._handleMouseMove, this);
44696         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44697         
44698         if (window.PointerEvent) {
44699             canvas.on('pointerdown', this._handleMouseDown, this);
44700             canvas.on('pointermove', this._handleMouseMove, this);
44701             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44702         }
44703         
44704         if ('ontouchstart' in window) {
44705             canvas.on('touchstart', this._handleTouchStart, this);
44706             canvas.on('touchmove', this._handleTouchMove, this);
44707             canvas.on('touchend', this._handleTouchEnd, this);
44708         }
44709         
44710         Roo.EventManager.onWindowResize(this.resize, this, true);
44711         
44712         // file input event
44713         this.fileEl().on('change', this.uploadImage, this);
44714         
44715         this.clear();
44716         
44717         this.resize();
44718     },
44719     
44720     resize: function(){
44721         
44722         var canvas = this.canvasEl().dom;
44723         var ctx = this.canvasElCtx();
44724         var img_data = false;
44725         
44726         if(canvas.width > 0) {
44727             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44728         }
44729         // setting canvas width will clean img data
44730         canvas.width = 0;
44731         
44732         var style = window.getComputedStyle ? 
44733             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44734             
44735         var padding_left = parseInt(style.paddingLeft) || 0;
44736         var padding_right = parseInt(style.paddingRight) || 0;
44737         
44738         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44739         
44740         if(img_data) {
44741             ctx.putImageData(img_data, 0, 0);
44742         }
44743     },
44744     
44745     _handleMouseDown: function(e)
44746     {
44747         if (e.browserEvent.which === 1) {
44748             this.mouse_btn_down = true;
44749             this.strokeBegin(e);
44750         }
44751     },
44752     
44753     _handleMouseMove: function (e)
44754     {
44755         if (this.mouse_btn_down) {
44756             this.strokeMoveUpdate(e);
44757         }
44758     },
44759     
44760     _handleMouseUp: function (e)
44761     {
44762         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44763             this.mouse_btn_down = false;
44764             this.strokeEnd(e);
44765         }
44766     },
44767     
44768     _handleTouchStart: function (e) {
44769         
44770         e.preventDefault();
44771         if (e.browserEvent.targetTouches.length === 1) {
44772             // var touch = e.browserEvent.changedTouches[0];
44773             // this.strokeBegin(touch);
44774             
44775              this.strokeBegin(e); // assume e catching the correct xy...
44776         }
44777     },
44778     
44779     _handleTouchMove: function (e) {
44780         e.preventDefault();
44781         // var touch = event.targetTouches[0];
44782         // _this._strokeMoveUpdate(touch);
44783         this.strokeMoveUpdate(e);
44784     },
44785     
44786     _handleTouchEnd: function (e) {
44787         var wasCanvasTouched = e.target === this.canvasEl().dom;
44788         if (wasCanvasTouched) {
44789             e.preventDefault();
44790             // var touch = event.changedTouches[0];
44791             // _this._strokeEnd(touch);
44792             this.strokeEnd(e);
44793         }
44794     },
44795     
44796     reset: function () {
44797         this._lastPoints = [];
44798         this._lastVelocity = 0;
44799         this._lastWidth = (this.min_width + this.max_width) / 2;
44800         this.canvasElCtx().fillStyle = this.dot_color;
44801     },
44802     
44803     strokeMoveUpdate: function(e)
44804     {
44805         this.strokeUpdate(e);
44806         
44807         if (this.throttle) {
44808             this.throttleStroke(this.strokeUpdate, this.throttle);
44809         }
44810         else {
44811             this.strokeUpdate(e);
44812         }
44813     },
44814     
44815     strokeBegin: function(e)
44816     {
44817         var newPointGroup = {
44818             color: this.dot_color,
44819             points: []
44820         };
44821         
44822         if (typeof this.onBegin === 'function') {
44823             this.onBegin(e);
44824         }
44825         
44826         this.curve_data.push(newPointGroup);
44827         this.reset();
44828         this.strokeUpdate(e);
44829     },
44830     
44831     strokeUpdate: function(e)
44832     {
44833         var rect = this.canvasEl().dom.getBoundingClientRect();
44834         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44835         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44836         var lastPoints = lastPointGroup.points;
44837         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44838         var isLastPointTooClose = lastPoint
44839             ? point.distanceTo(lastPoint) <= this.min_distance
44840             : false;
44841         var color = lastPointGroup.color;
44842         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44843             var curve = this.addPoint(point);
44844             if (!lastPoint) {
44845                 this.drawDot({color: color, point: point});
44846             }
44847             else if (curve) {
44848                 this.drawCurve({color: color, curve: curve});
44849             }
44850             lastPoints.push({
44851                 time: point.time,
44852                 x: point.x,
44853                 y: point.y
44854             });
44855         }
44856     },
44857     
44858     strokeEnd: function(e)
44859     {
44860         this.strokeUpdate(e);
44861         if (typeof this.onEnd === 'function') {
44862             this.onEnd(e);
44863         }
44864     },
44865     
44866     addPoint:  function (point) {
44867         var _lastPoints = this._lastPoints;
44868         _lastPoints.push(point);
44869         if (_lastPoints.length > 2) {
44870             if (_lastPoints.length === 3) {
44871                 _lastPoints.unshift(_lastPoints[0]);
44872             }
44873             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44874             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44875             _lastPoints.shift();
44876             return curve;
44877         }
44878         return null;
44879     },
44880     
44881     calculateCurveWidths: function (startPoint, endPoint) {
44882         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44883             (1 - this.velocity_filter_weight) * this._lastVelocity;
44884
44885         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44886         var widths = {
44887             end: newWidth,
44888             start: this._lastWidth
44889         };
44890         
44891         this._lastVelocity = velocity;
44892         this._lastWidth = newWidth;
44893         return widths;
44894     },
44895     
44896     drawDot: function (_a) {
44897         var color = _a.color, point = _a.point;
44898         var ctx = this.canvasElCtx();
44899         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44900         ctx.beginPath();
44901         this.drawCurveSegment(point.x, point.y, width);
44902         ctx.closePath();
44903         ctx.fillStyle = color;
44904         ctx.fill();
44905     },
44906     
44907     drawCurve: function (_a) {
44908         var color = _a.color, curve = _a.curve;
44909         var ctx = this.canvasElCtx();
44910         var widthDelta = curve.endWidth - curve.startWidth;
44911         var drawSteps = Math.floor(curve.length()) * 2;
44912         ctx.beginPath();
44913         ctx.fillStyle = color;
44914         for (var i = 0; i < drawSteps; i += 1) {
44915         var t = i / drawSteps;
44916         var tt = t * t;
44917         var ttt = tt * t;
44918         var u = 1 - t;
44919         var uu = u * u;
44920         var uuu = uu * u;
44921         var x = uuu * curve.startPoint.x;
44922         x += 3 * uu * t * curve.control1.x;
44923         x += 3 * u * tt * curve.control2.x;
44924         x += ttt * curve.endPoint.x;
44925         var y = uuu * curve.startPoint.y;
44926         y += 3 * uu * t * curve.control1.y;
44927         y += 3 * u * tt * curve.control2.y;
44928         y += ttt * curve.endPoint.y;
44929         var width = curve.startWidth + ttt * widthDelta;
44930         this.drawCurveSegment(x, y, width);
44931         }
44932         ctx.closePath();
44933         ctx.fill();
44934     },
44935     
44936     drawCurveSegment: function (x, y, width) {
44937         var ctx = this.canvasElCtx();
44938         ctx.moveTo(x, y);
44939         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44940         this.is_empty = false;
44941     },
44942     
44943     clear: function()
44944     {
44945         var ctx = this.canvasElCtx();
44946         var canvas = this.canvasEl().dom;
44947         ctx.fillStyle = this.bg_color;
44948         ctx.clearRect(0, 0, canvas.width, canvas.height);
44949         ctx.fillRect(0, 0, canvas.width, canvas.height);
44950         this.curve_data = [];
44951         this.reset();
44952         this.is_empty = true;
44953     },
44954     
44955     fileEl: function()
44956     {
44957         return  this.el.select('input',true).first();
44958     },
44959     
44960     canvasEl: function()
44961     {
44962         return this.el.select('canvas',true).first();
44963     },
44964     
44965     canvasElCtx: function()
44966     {
44967         return this.el.select('canvas',true).first().dom.getContext('2d');
44968     },
44969     
44970     getImage: function(type)
44971     {
44972         if(this.is_empty) {
44973             return false;
44974         }
44975         
44976         // encryption ?
44977         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44978     },
44979     
44980     drawFromImage: function(img_src)
44981     {
44982         var img = new Image();
44983         
44984         img.onload = function(){
44985             this.canvasElCtx().drawImage(img, 0, 0);
44986         }.bind(this);
44987         
44988         img.src = img_src;
44989         
44990         this.is_empty = false;
44991     },
44992     
44993     selectImage: function()
44994     {
44995         this.fileEl().dom.click();
44996     },
44997     
44998     uploadImage: function(e)
44999     {
45000         var reader = new FileReader();
45001         
45002         reader.onload = function(e){
45003             var img = new Image();
45004             img.onload = function(){
45005                 this.reset();
45006                 this.canvasElCtx().drawImage(img, 0, 0);
45007             }.bind(this);
45008             img.src = e.target.result;
45009         }.bind(this);
45010         
45011         reader.readAsDataURL(e.target.files[0]);
45012     },
45013     
45014     // Bezier Point Constructor
45015     Point: (function () {
45016         function Point(x, y, time) {
45017             this.x = x;
45018             this.y = y;
45019             this.time = time || Date.now();
45020         }
45021         Point.prototype.distanceTo = function (start) {
45022             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45023         };
45024         Point.prototype.equals = function (other) {
45025             return this.x === other.x && this.y === other.y && this.time === other.time;
45026         };
45027         Point.prototype.velocityFrom = function (start) {
45028             return this.time !== start.time
45029             ? this.distanceTo(start) / (this.time - start.time)
45030             : 0;
45031         };
45032         return Point;
45033     }()),
45034     
45035     
45036     // Bezier Constructor
45037     Bezier: (function () {
45038         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45039             this.startPoint = startPoint;
45040             this.control2 = control2;
45041             this.control1 = control1;
45042             this.endPoint = endPoint;
45043             this.startWidth = startWidth;
45044             this.endWidth = endWidth;
45045         }
45046         Bezier.fromPoints = function (points, widths, scope) {
45047             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45048             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45049             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45050         };
45051         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45052             var dx1 = s1.x - s2.x;
45053             var dy1 = s1.y - s2.y;
45054             var dx2 = s2.x - s3.x;
45055             var dy2 = s2.y - s3.y;
45056             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45057             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45058             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45059             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45060             var dxm = m1.x - m2.x;
45061             var dym = m1.y - m2.y;
45062             var k = l2 / (l1 + l2);
45063             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45064             var tx = s2.x - cm.x;
45065             var ty = s2.y - cm.y;
45066             return {
45067                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45068                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45069             };
45070         };
45071         Bezier.prototype.length = function () {
45072             var steps = 10;
45073             var length = 0;
45074             var px;
45075             var py;
45076             for (var i = 0; i <= steps; i += 1) {
45077                 var t = i / steps;
45078                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45079                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45080                 if (i > 0) {
45081                     var xdiff = cx - px;
45082                     var ydiff = cy - py;
45083                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45084                 }
45085                 px = cx;
45086                 py = cy;
45087             }
45088             return length;
45089         };
45090         Bezier.prototype.point = function (t, start, c1, c2, end) {
45091             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45092             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45093             + (3.0 * c2 * (1.0 - t) * t * t)
45094             + (end * t * t * t);
45095         };
45096         return Bezier;
45097     }()),
45098     
45099     throttleStroke: function(fn, wait) {
45100       if (wait === void 0) { wait = 250; }
45101       var previous = 0;
45102       var timeout = null;
45103       var result;
45104       var storedContext;
45105       var storedArgs;
45106       var later = function () {
45107           previous = Date.now();
45108           timeout = null;
45109           result = fn.apply(storedContext, storedArgs);
45110           if (!timeout) {
45111               storedContext = null;
45112               storedArgs = [];
45113           }
45114       };
45115       return function wrapper() {
45116           var args = [];
45117           for (var _i = 0; _i < arguments.length; _i++) {
45118               args[_i] = arguments[_i];
45119           }
45120           var now = Date.now();
45121           var remaining = wait - (now - previous);
45122           storedContext = this;
45123           storedArgs = args;
45124           if (remaining <= 0 || remaining > wait) {
45125               if (timeout) {
45126                   clearTimeout(timeout);
45127                   timeout = null;
45128               }
45129               previous = now;
45130               result = fn.apply(storedContext, storedArgs);
45131               if (!timeout) {
45132                   storedContext = null;
45133                   storedArgs = [];
45134               }
45135           }
45136           else if (!timeout) {
45137               timeout = window.setTimeout(later, remaining);
45138           }
45139           return result;
45140       };
45141   }
45142   
45143 });
45144
45145  
45146
45147