docs/Roo.docs.bjs
[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  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  Roo.bootstrap.menu = Roo.bootstrap.menu || {};
3425 /**
3426  * @class Roo.bootstrap.MenuMgr
3427  * @licence LGPL
3428  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3429  * @singleton
3430  */
3431 Roo.bootstrap.menu.Manager = function(){
3432    var menus, active, groups = {}, attached = false, lastShow = new Date();
3433
3434    // private - called when first menu is created
3435    function init(){
3436        menus = {};
3437        active = new Roo.util.MixedCollection();
3438        Roo.get(document).addKeyListener(27, function(){
3439            if(active.length > 0){
3440                hideAll();
3441            }
3442        });
3443    }
3444
3445    // private
3446    function hideAll(){
3447        if(active && active.length > 0){
3448            var c = active.clone();
3449            c.each(function(m){
3450                m.hide();
3451            });
3452        }
3453    }
3454
3455    // private
3456    function onHide(m){
3457        active.remove(m);
3458        if(active.length < 1){
3459            Roo.get(document).un("mouseup", onMouseDown);
3460             
3461            attached = false;
3462        }
3463    }
3464
3465    // private
3466    function onShow(m){
3467        var last = active.last();
3468        lastShow = new Date();
3469        active.add(m);
3470        if(!attached){
3471           Roo.get(document).on("mouseup", onMouseDown);
3472            
3473            attached = true;
3474        }
3475        if(m.parentMenu){
3476           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3477           m.parentMenu.activeChild = m;
3478        }else if(last && last.isVisible()){
3479           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3480        }
3481    }
3482
3483    // private
3484    function onBeforeHide(m){
3485        if(m.activeChild){
3486            m.activeChild.hide();
3487        }
3488        if(m.autoHideTimer){
3489            clearTimeout(m.autoHideTimer);
3490            delete m.autoHideTimer;
3491        }
3492    }
3493
3494    // private
3495    function onBeforeShow(m){
3496        var pm = m.parentMenu;
3497        if(!pm && !m.allowOtherMenus){
3498            hideAll();
3499        }else if(pm && pm.activeChild && active != m){
3500            pm.activeChild.hide();
3501        }
3502    }
3503
3504    // private this should really trigger on mouseup..
3505    function onMouseDown(e){
3506         Roo.log("on Mouse Up");
3507         
3508         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3509             Roo.log("MenuManager hideAll");
3510             hideAll();
3511             e.stopEvent();
3512         }
3513         
3514         
3515    }
3516
3517    // private
3518    function onBeforeCheck(mi, state){
3519        if(state){
3520            var g = groups[mi.group];
3521            for(var i = 0, l = g.length; i < l; i++){
3522                if(g[i] != mi){
3523                    g[i].setChecked(false);
3524                }
3525            }
3526        }
3527    }
3528
3529    return {
3530
3531        /**
3532         * Hides all menus that are currently visible
3533         */
3534        hideAll : function(){
3535             hideAll();  
3536        },
3537
3538        // private
3539        register : function(menu){
3540            if(!menus){
3541                init();
3542            }
3543            menus[menu.id] = menu;
3544            menu.on("beforehide", onBeforeHide);
3545            menu.on("hide", onHide);
3546            menu.on("beforeshow", onBeforeShow);
3547            menu.on("show", onShow);
3548            var g = menu.group;
3549            if(g && menu.events["checkchange"]){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menu);
3554                menu.on("checkchange", onCheck);
3555            }
3556        },
3557
3558         /**
3559          * Returns a {@link Roo.menu.Menu} object
3560          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3561          * be used to generate and return a new Menu instance.
3562          */
3563        get : function(menu){
3564            if(typeof menu == "string"){ // menu id
3565                return menus[menu];
3566            }else if(menu.events){  // menu instance
3567                return menu;
3568            }
3569            /*else if(typeof menu.length == 'number'){ // array of menu items?
3570                return new Roo.bootstrap.Menu({items:menu});
3571            }else{ // otherwise, must be a config
3572                return new Roo.bootstrap.Menu(menu);
3573            }
3574            */
3575            return false;
3576        },
3577
3578        // private
3579        unregister : function(menu){
3580            delete menus[menu.id];
3581            menu.un("beforehide", onBeforeHide);
3582            menu.un("hide", onHide);
3583            menu.un("beforeshow", onBeforeShow);
3584            menu.un("show", onShow);
3585            var g = menu.group;
3586            if(g && menu.events["checkchange"]){
3587                groups[g].remove(menu);
3588                menu.un("checkchange", onCheck);
3589            }
3590        },
3591
3592        // private
3593        registerCheckable : function(menuItem){
3594            var g = menuItem.group;
3595            if(g){
3596                if(!groups[g]){
3597                    groups[g] = [];
3598                }
3599                groups[g].push(menuItem);
3600                menuItem.on("beforecheckchange", onBeforeCheck);
3601            }
3602        },
3603
3604        // private
3605        unregisterCheckable : function(menuItem){
3606            var g = menuItem.group;
3607            if(g){
3608                groups[g].remove(menuItem);
3609                menuItem.un("beforecheckchange", onBeforeCheck);
3610            }
3611        }
3612    };
3613 }(); 
3614 /**
3615  * @class Roo.bootstrap.menu.Menu
3616  * @extends Roo.bootstrap.Component
3617  * @licence LGPL
3618  * @children Roo.bootstrap.menu.Item
3619  * Bootstrap Menu class - container for MenuItems
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @licence LGPL
4094  * Bootstrap MenuItem class
4095  * 
4096  * @cfg {String} html the menu label
4097  * @cfg {String} href the link
4098  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4099  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4100  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4101  * @cfg {String} fa favicon to show on left of menu item.
4102  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4103  * 
4104  * 
4105  * @constructor
4106  * Create a new MenuItem
4107  * @param {Object} config The config object
4108  */
4109
4110
4111 Roo.bootstrap.menu.Item = function(config){
4112     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4113     this.addEvents({
4114         // raw events
4115         /**
4116          * @event click
4117          * The raw click event for the entire grid.
4118          * @param {Roo.bootstrap.menu.Item} this
4119          * @param {Roo.EventObject} e
4120          */
4121         "click" : true
4122     });
4123 };
4124
4125 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4126     
4127     href : false,
4128     html : false,
4129     preventDefault: false,
4130     isContainer : false,
4131     active : false,
4132     fa: false,
4133     
4134     getAutoCreate : function(){
4135         
4136         if(this.isContainer){
4137             return {
4138                 tag: 'li',
4139                 cls: 'dropdown-menu-item '
4140             };
4141         }
4142         var ctag = {
4143             tag: 'span',
4144             html: 'Link'
4145         };
4146         
4147         var anc = {
4148             tag : 'a',
4149             cls : 'dropdown-item',
4150             href : '#',
4151             cn : [  ]
4152         };
4153         
4154         if (this.fa !== false) {
4155             anc.cn.push({
4156                 tag : 'i',
4157                 cls : 'fa fa-' + this.fa
4158             });
4159         }
4160         
4161         anc.cn.push(ctag);
4162         
4163         
4164         var cfg= {
4165             tag: 'li',
4166             cls: 'dropdown-menu-item',
4167             cn: [ anc ]
4168         };
4169         if (this.parent().type == 'treeview') {
4170             cfg.cls = 'treeview-menu';
4171         }
4172         if (this.active) {
4173             cfg.cls += ' active';
4174         }
4175         
4176         
4177         
4178         anc.href = this.href || cfg.cn[0].href ;
4179         ctag.html = this.html || cfg.cn[0].html ;
4180         return cfg;
4181     },
4182     
4183     initEvents: function()
4184     {
4185         if (this.parent().type == 'treeview') {
4186             this.el.select('a').on('click', this.onClick, this);
4187         }
4188         
4189         if (this.menu) {
4190             this.menu.parentType = this.xtype;
4191             this.menu.triggerEl = this.el;
4192             this.menu = this.addxtype(Roo.apply({}, this.menu));
4193         }
4194         
4195     },
4196     onClick : function(e)
4197     {
4198         Roo.log('item on click ');
4199         
4200         if(this.preventDefault){
4201             e.preventDefault();
4202         }
4203         //this.parent().hideMenuItems();
4204         
4205         this.fireEvent('click', this, e);
4206     },
4207     getEl : function()
4208     {
4209         return this.el;
4210     } 
4211 });
4212
4213  
4214
4215  
4216
4217   
4218 /**
4219  * @class Roo.bootstrap.menu.Separator
4220  * @extends Roo.bootstrap.Component
4221  * @licence LGPL
4222  * Bootstrap Separator class
4223  * 
4224  * @constructor
4225  * Create a new Separator
4226  * @param {Object} config The config object
4227  */
4228
4229
4230 Roo.bootstrap.menu.Separator = function(config){
4231     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4232 };
4233
4234 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4235     
4236     getAutoCreate : function(){
4237         var cfg = {
4238             tag : 'li',
4239             cls: 'dropdown-divider divider'
4240         };
4241         
4242         return cfg;
4243     }
4244    
4245 });
4246
4247  
4248
4249  // deprciated 
4250 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
4251 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
4252 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
4253
4254
4255 /*
4256 * Licence: LGPL
4257 */
4258
4259 /**
4260  * @class Roo.bootstrap.Modal
4261  * @extends Roo.bootstrap.Component
4262  * @builder-top
4263  * @parent none
4264  * @children Roo.bootstrap.Component
4265  * Bootstrap Modal class
4266  * @cfg {String} title Title of dialog
4267  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4268  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4269  * @cfg {Boolean} specificTitle default false
4270  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4271  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4272  * @cfg {Boolean} animate default true
4273  * @cfg {Boolean} allow_close default true
4274  * @cfg {Boolean} fitwindow default false
4275  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4276  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4277  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4278  * @cfg {String} size (sm|lg|xl) default empty
4279  * @cfg {Number} max_width set the max width of modal
4280  * @cfg {Boolean} editableTitle can the title be edited
4281
4282  *
4283  *
4284  * @constructor
4285  * Create a new Modal Dialog
4286  * @param {Object} config The config object
4287  */
4288
4289 Roo.bootstrap.Modal = function(config){
4290     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291     this.addEvents({
4292         // raw events
4293         /**
4294          * @event btnclick
4295          * The raw btnclick event for the button
4296          * @param {Roo.EventObject} e
4297          */
4298         "btnclick" : true,
4299         /**
4300          * @event resize
4301          * Fire when dialog resize
4302          * @param {Roo.bootstrap.Modal} this
4303          * @param {Roo.EventObject} e
4304          */
4305         "resize" : true,
4306         /**
4307          * @event titlechanged
4308          * Fire when the editable title has been changed
4309          * @param {Roo.bootstrap.Modal} this
4310          * @param {Roo.EventObject} value
4311          */
4312         "titlechanged" : true 
4313         
4314     });
4315     this.buttons = this.buttons || [];
4316
4317     if (this.tmpl) {
4318         this.tmpl = Roo.factory(this.tmpl);
4319     }
4320
4321 };
4322
4323 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4324
4325     title : 'test dialog',
4326
4327     buttons : false,
4328
4329     // set on load...
4330
4331     html: false,
4332
4333     tmp: false,
4334
4335     specificTitle: false,
4336
4337     buttonPosition: 'right',
4338
4339     allow_close : true,
4340
4341     animate : true,
4342
4343     fitwindow: false,
4344     
4345      // private
4346     dialogEl: false,
4347     bodyEl:  false,
4348     footerEl:  false,
4349     titleEl:  false,
4350     closeEl:  false,
4351
4352     size: '',
4353     
4354     max_width: 0,
4355     
4356     max_height: 0,
4357     
4358     fit_content: false,
4359     editableTitle  : false,
4360
4361     onRender : function(ct, position)
4362     {
4363         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364
4365         if(!this.el){
4366             var cfg = Roo.apply({},  this.getAutoCreate());
4367             cfg.id = Roo.id();
4368             //if(!cfg.name){
4369             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4370             //}
4371             //if (!cfg.name.length) {
4372             //    delete cfg.name;
4373            // }
4374             if (this.cls) {
4375                 cfg.cls += ' ' + this.cls;
4376             }
4377             if (this.style) {
4378                 cfg.style = this.style;
4379             }
4380             this.el = Roo.get(document.body).createChild(cfg, position);
4381         }
4382         //var type = this.el.dom.type;
4383
4384
4385         if(this.tabIndex !== undefined){
4386             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387         }
4388
4389         this.dialogEl = this.el.select('.modal-dialog',true).first();
4390         this.bodyEl = this.el.select('.modal-body',true).first();
4391         this.closeEl = this.el.select('.modal-header .close', true).first();
4392         this.headerEl = this.el.select('.modal-header',true).first();
4393         this.titleEl = this.el.select('.modal-title',true).first();
4394         this.footerEl = this.el.select('.modal-footer',true).first();
4395
4396         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4397         
4398         //this.el.addClass("x-dlg-modal");
4399
4400         if (this.buttons.length) {
4401             Roo.each(this.buttons, function(bb) {
4402                 var b = Roo.apply({}, bb);
4403                 b.xns = b.xns || Roo.bootstrap;
4404                 b.xtype = b.xtype || 'Button';
4405                 if (typeof(b.listeners) == 'undefined') {
4406                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4407                 }
4408
4409                 var btn = Roo.factory(b);
4410
4411                 btn.render(this.getButtonContainer());
4412
4413             },this);
4414         }
4415         // render the children.
4416         var nitems = [];
4417
4418         if(typeof(this.items) != 'undefined'){
4419             var items = this.items;
4420             delete this.items;
4421
4422             for(var i =0;i < items.length;i++) {
4423                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4424             }
4425         }
4426
4427         this.items = nitems;
4428
4429         // where are these used - they used to be body/close/footer
4430
4431
4432         this.initEvents();
4433         //this.el.addClass([this.fieldClass, this.cls]);
4434
4435     },
4436
4437     getAutoCreate : function()
4438     {
4439         // we will default to modal-body-overflow - might need to remove or make optional later.
4440         var bdy = {
4441                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4442                 html : this.html || ''
4443         };
4444
4445         var title = {
4446             tag: 'h5',
4447             cls : 'modal-title',
4448             html : this.title
4449         };
4450
4451         if(this.specificTitle){ // WTF is this?
4452             title = this.title;
4453         }
4454
4455         var header = [];
4456         if (this.allow_close && Roo.bootstrap.version == 3) {
4457             header.push({
4458                 tag: 'button',
4459                 cls : 'close',
4460                 html : '&times'
4461             });
4462         }
4463
4464         header.push(title);
4465
4466         if (this.editableTitle) {
4467             header.push({
4468                 cls: 'form-control roo-editable-title d-none',
4469                 tag: 'input',
4470                 type: 'text'
4471             });
4472         }
4473         
4474         if (this.allow_close && Roo.bootstrap.version == 4) {
4475             header.push({
4476                 tag: 'button',
4477                 cls : 'close',
4478                 html : '&times'
4479             });
4480         }
4481         
4482         var size = '';
4483
4484         if(this.size.length){
4485             size = 'modal-' + this.size;
4486         }
4487         
4488         var footer = Roo.bootstrap.version == 3 ?
4489             {
4490                 cls : 'modal-footer',
4491                 cn : [
4492                     {
4493                         tag: 'div',
4494                         cls: 'btn-' + this.buttonPosition
4495                     }
4496                 ]
4497
4498             } :
4499             {  // BS4 uses mr-auto on left buttons....
4500                 cls : 'modal-footer'
4501             };
4502
4503             
4504
4505         
4506         
4507         var modal = {
4508             cls: "modal",
4509              cn : [
4510                 {
4511                     cls: "modal-dialog " + size,
4512                     cn : [
4513                         {
4514                             cls : "modal-content",
4515                             cn : [
4516                                 {
4517                                     cls : 'modal-header',
4518                                     cn : header
4519                                 },
4520                                 bdy,
4521                                 footer
4522                             ]
4523
4524                         }
4525                     ]
4526
4527                 }
4528             ]
4529         };
4530
4531         if(this.animate){
4532             modal.cls += ' fade';
4533         }
4534
4535         return modal;
4536
4537     },
4538     getChildContainer : function() {
4539
4540          return this.bodyEl;
4541
4542     },
4543     getButtonContainer : function() {
4544         
4545          return Roo.bootstrap.version == 4 ?
4546             this.el.select('.modal-footer',true).first()
4547             : this.el.select('.modal-footer div',true).first();
4548
4549     },
4550     initEvents : function()
4551     {
4552         if (this.allow_close) {
4553             this.closeEl.on('click', this.hide, this);
4554         }
4555         Roo.EventManager.onWindowResize(this.resize, this, true);
4556         if (this.editableTitle) {
4557             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4558             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559             this.headerEditEl.on('keyup', function(e) {
4560                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561                         this.toggleHeaderInput(false)
4562                     }
4563                 }, this);
4564             this.headerEditEl.on('blur', function(e) {
4565                 this.toggleHeaderInput(false)
4566             },this);
4567         }
4568
4569     },
4570   
4571
4572     resize : function()
4573     {
4574         this.maskEl.setSize(
4575             Roo.lib.Dom.getViewWidth(true),
4576             Roo.lib.Dom.getViewHeight(true)
4577         );
4578         
4579         if (this.fitwindow) {
4580             
4581            this.dialogEl.setStyle( { 'max-width' : '100%' });
4582             this.setSize(
4583                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4585             );
4586             return;
4587         }
4588         
4589         if(this.max_width !== 0) {
4590             
4591             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592             
4593             if(this.height) {
4594                 this.setSize(w, this.height);
4595                 return;
4596             }
4597             
4598             if(this.max_height) {
4599                 this.setSize(w,Math.min(
4600                     this.max_height,
4601                     Roo.lib.Dom.getViewportHeight(true) - 60
4602                 ));
4603                 
4604                 return;
4605             }
4606             
4607             if(!this.fit_content) {
4608                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4609                 return;
4610             }
4611             
4612             this.setSize(w, Math.min(
4613                 60 +
4614                 this.headerEl.getHeight() + 
4615                 this.footerEl.getHeight() + 
4616                 this.getChildHeight(this.bodyEl.dom.childNodes),
4617                 Roo.lib.Dom.getViewportHeight(true) - 60)
4618             );
4619         }
4620         
4621     },
4622
4623     setSize : function(w,h)
4624     {
4625         if (!w && !h) {
4626             return;
4627         }
4628         
4629         this.resizeTo(w,h);
4630     },
4631
4632     show : function() {
4633
4634         if (!this.rendered) {
4635             this.render();
4636         }
4637         this.toggleHeaderInput(false);
4638         //this.el.setStyle('display', 'block');
4639         this.el.removeClass('hideing');
4640         this.el.dom.style.display='block';
4641         
4642         Roo.get(document.body).addClass('modal-open');
4643  
4644         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4645             
4646             (function(){
4647                 this.el.addClass('show');
4648                 this.el.addClass('in');
4649             }).defer(50, this);
4650         }else{
4651             this.el.addClass('show');
4652             this.el.addClass('in');
4653         }
4654
4655         // not sure how we can show data in here..
4656         //if (this.tmpl) {
4657         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4658         //}
4659
4660         Roo.get(document.body).addClass("x-body-masked");
4661         
4662         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4663         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664         this.maskEl.dom.style.display = 'block';
4665         this.maskEl.addClass('show');
4666         
4667         
4668         this.resize();
4669         
4670         this.fireEvent('show', this);
4671
4672         // set zindex here - otherwise it appears to be ignored...
4673         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4674
4675         (function () {
4676             this.items.forEach( function(e) {
4677                 e.layout ? e.layout() : false;
4678
4679             });
4680         }).defer(100,this);
4681
4682     },
4683     hide : function()
4684     {
4685         if(this.fireEvent("beforehide", this) !== false){
4686             
4687             this.maskEl.removeClass('show');
4688             
4689             this.maskEl.dom.style.display = '';
4690             Roo.get(document.body).removeClass("x-body-masked");
4691             this.el.removeClass('in');
4692             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4693
4694             if(this.animate){ // why
4695                 this.el.addClass('hideing');
4696                 this.el.removeClass('show');
4697                 (function(){
4698                     if (!this.el.hasClass('hideing')) {
4699                         return; // it's been shown again...
4700                     }
4701                     
4702                     this.el.dom.style.display='';
4703
4704                     Roo.get(document.body).removeClass('modal-open');
4705                     this.el.removeClass('hideing');
4706                 }).defer(150,this);
4707                 
4708             }else{
4709                 this.el.removeClass('show');
4710                 this.el.dom.style.display='';
4711                 Roo.get(document.body).removeClass('modal-open');
4712
4713             }
4714             this.fireEvent('hide', this);
4715         }
4716     },
4717     isVisible : function()
4718     {
4719         
4720         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4721         
4722     },
4723
4724     addButton : function(str, cb)
4725     {
4726
4727
4728         var b = Roo.apply({}, { html : str } );
4729         b.xns = b.xns || Roo.bootstrap;
4730         b.xtype = b.xtype || 'Button';
4731         if (typeof(b.listeners) == 'undefined') {
4732             b.listeners = { click : cb.createDelegate(this)  };
4733         }
4734
4735         var btn = Roo.factory(b);
4736
4737         btn.render(this.getButtonContainer());
4738
4739         return btn;
4740
4741     },
4742
4743     setDefaultButton : function(btn)
4744     {
4745         //this.el.select('.modal-footer').()
4746     },
4747
4748     resizeTo: function(w,h)
4749     {
4750         this.dialogEl.setWidth(w);
4751         
4752         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4753
4754         this.bodyEl.setHeight(h - diff);
4755         
4756         this.fireEvent('resize', this);
4757     },
4758     
4759     setContentSize  : function(w, h)
4760     {
4761
4762     },
4763     onButtonClick: function(btn,e)
4764     {
4765         //Roo.log([a,b,c]);
4766         this.fireEvent('btnclick', btn.name, e);
4767     },
4768      /**
4769      * Set the title of the Dialog
4770      * @param {String} str new Title
4771      */
4772     setTitle: function(str) {
4773         this.titleEl.dom.innerHTML = str;
4774         this.title = str;
4775     },
4776     /**
4777      * Set the body of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setBody: function(str) {
4781         this.bodyEl.dom.innerHTML = str;
4782     },
4783     /**
4784      * Set the body of the Dialog using the template
4785      * @param {Obj} data - apply this data to the template and replace the body contents.
4786      */
4787     applyBody: function(obj)
4788     {
4789         if (!this.tmpl) {
4790             Roo.log("Error - using apply Body without a template");
4791             //code
4792         }
4793         this.tmpl.overwrite(this.bodyEl, obj);
4794     },
4795     
4796     getChildHeight : function(child_nodes)
4797     {
4798         if(
4799             !child_nodes ||
4800             child_nodes.length == 0
4801         ) {
4802             return 0;
4803         }
4804         
4805         var child_height = 0;
4806         
4807         for(var i = 0; i < child_nodes.length; i++) {
4808             
4809             /*
4810             * for modal with tabs...
4811             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4812                 
4813                 var layout_childs = child_nodes[i].childNodes;
4814                 
4815                 for(var j = 0; j < layout_childs.length; j++) {
4816                     
4817                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4818                         
4819                         var layout_body_childs = layout_childs[j].childNodes;
4820                         
4821                         for(var k = 0; k < layout_body_childs.length; k++) {
4822                             
4823                             if(layout_body_childs[k].classList.contains('navbar')) {
4824                                 child_height += layout_body_childs[k].offsetHeight;
4825                                 continue;
4826                             }
4827                             
4828                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4829                                 
4830                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4831                                 
4832                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4833                                     
4834                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4835                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4836                                         continue;
4837                                     }
4838                                     
4839                                 }
4840                                 
4841                             }
4842                             
4843                         }
4844                     }
4845                 }
4846                 continue;
4847             }
4848             */
4849             
4850             child_height += child_nodes[i].offsetHeight;
4851             // Roo.log(child_nodes[i].offsetHeight);
4852         }
4853         
4854         return child_height;
4855     },
4856     toggleHeaderInput : function(is_edit)
4857     {
4858         if (!this.editableTitle) {
4859             return; // not editable.
4860         }
4861         if (is_edit && this.is_header_editing) {
4862             return; // already editing..
4863         }
4864         if (is_edit) {
4865     
4866             this.headerEditEl.dom.value = this.title;
4867             this.headerEditEl.removeClass('d-none');
4868             this.headerEditEl.dom.focus();
4869             this.titleEl.addClass('d-none');
4870             
4871             this.is_header_editing = true;
4872             return
4873         }
4874         // flip back to not editing.
4875         this.title = this.headerEditEl.dom.value;
4876         this.headerEditEl.addClass('d-none');
4877         this.titleEl.removeClass('d-none');
4878         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4879         this.is_header_editing = false;
4880         this.fireEvent('titlechanged', this, this.title);
4881     
4882             
4883         
4884     }
4885
4886 });
4887
4888
4889 Roo.apply(Roo.bootstrap.Modal,  {
4890     /**
4891          * Button config that displays a single OK button
4892          * @type Object
4893          */
4894         OK :  [{
4895             name : 'ok',
4896             weight : 'primary',
4897             html : 'OK'
4898         }],
4899         /**
4900          * Button config that displays Yes and No buttons
4901          * @type Object
4902          */
4903         YESNO : [
4904             {
4905                 name  : 'no',
4906                 html : 'No'
4907             },
4908             {
4909                 name  :'yes',
4910                 weight : 'primary',
4911                 html : 'Yes'
4912             }
4913         ],
4914
4915         /**
4916          * Button config that displays OK and Cancel buttons
4917          * @type Object
4918          */
4919         OKCANCEL : [
4920             {
4921                name : 'cancel',
4922                 html : 'Cancel'
4923             },
4924             {
4925                 name : 'ok',
4926                 weight : 'primary',
4927                 html : 'OK'
4928             }
4929         ],
4930         /**
4931          * Button config that displays Yes, No and Cancel buttons
4932          * @type Object
4933          */
4934         YESNOCANCEL : [
4935             {
4936                 name : 'yes',
4937                 weight : 'primary',
4938                 html : 'Yes'
4939             },
4940             {
4941                 name : 'no',
4942                 html : 'No'
4943             },
4944             {
4945                 name : 'cancel',
4946                 html : 'Cancel'
4947             }
4948         ],
4949         
4950         zIndex : 10001
4951 });
4952
4953 /*
4954  * - LGPL
4955  *
4956  * messagebox - can be used as a replace
4957  * 
4958  */
4959 /**
4960  * @class Roo.MessageBox
4961  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4962  * Example usage:
4963  *<pre><code>
4964 // Basic alert:
4965 Roo.Msg.alert('Status', 'Changes saved successfully.');
4966
4967 // Prompt for user data:
4968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4969     if (btn == 'ok'){
4970         // process text value...
4971     }
4972 });
4973
4974 // Show a dialog using config options:
4975 Roo.Msg.show({
4976    title:'Save Changes?',
4977    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4978    buttons: Roo.Msg.YESNOCANCEL,
4979    fn: processResult,
4980    animEl: 'elId'
4981 });
4982 </code></pre>
4983  * @singleton
4984  */
4985 Roo.bootstrap.MessageBox = function(){
4986     var dlg, opt, mask, waitTimer;
4987     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4988     var buttons, activeTextEl, bwidth;
4989
4990     
4991     // private
4992     var handleButton = function(button){
4993         dlg.hide();
4994         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4995     };
4996
4997     // private
4998     var handleHide = function(){
4999         if(opt && opt.cls){
5000             dlg.el.removeClass(opt.cls);
5001         }
5002         //if(waitTimer){
5003         //    Roo.TaskMgr.stop(waitTimer);
5004         //    waitTimer = null;
5005         //}
5006     };
5007
5008     // private
5009     var updateButtons = function(b){
5010         var width = 0;
5011         if(!b){
5012             buttons["ok"].hide();
5013             buttons["cancel"].hide();
5014             buttons["yes"].hide();
5015             buttons["no"].hide();
5016             dlg.footerEl.hide();
5017             
5018             return width;
5019         }
5020         dlg.footerEl.show();
5021         for(var k in buttons){
5022             if(typeof buttons[k] != "function"){
5023                 if(b[k]){
5024                     buttons[k].show();
5025                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5026                     width += buttons[k].el.getWidth()+15;
5027                 }else{
5028                     buttons[k].hide();
5029                 }
5030             }
5031         }
5032         return width;
5033     };
5034
5035     // private
5036     var handleEsc = function(d, k, e){
5037         if(opt && opt.closable !== false){
5038             dlg.hide();
5039         }
5040         if(e){
5041             e.stopEvent();
5042         }
5043     };
5044
5045     return {
5046         /**
5047          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5048          * @return {Roo.BasicDialog} The BasicDialog element
5049          */
5050         getDialog : function(){
5051            if(!dlg){
5052                 dlg = new Roo.bootstrap.Modal( {
5053                     //draggable: true,
5054                     //resizable:false,
5055                     //constraintoviewport:false,
5056                     //fixedcenter:true,
5057                     //collapsible : false,
5058                     //shim:true,
5059                     //modal: true,
5060                 //    width: 'auto',
5061                   //  height:100,
5062                     //buttonAlign:"center",
5063                     closeClick : function(){
5064                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5065                             handleButton("no");
5066                         }else{
5067                             handleButton("cancel");
5068                         }
5069                     }
5070                 });
5071                 dlg.render();
5072                 dlg.on("hide", handleHide);
5073                 mask = dlg.mask;
5074                 //dlg.addKeyListener(27, handleEsc);
5075                 buttons = {};
5076                 this.buttons = buttons;
5077                 var bt = this.buttonText;
5078                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5079                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5080                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5081                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5082                 //Roo.log(buttons);
5083                 bodyEl = dlg.bodyEl.createChild({
5084
5085                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5086                         '<textarea class="roo-mb-textarea"></textarea>' +
5087                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5088                 });
5089                 msgEl = bodyEl.dom.firstChild;
5090                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5091                 textboxEl.enableDisplayMode();
5092                 textboxEl.addKeyListener([10,13], function(){
5093                     if(dlg.isVisible() && opt && opt.buttons){
5094                         if(opt.buttons.ok){
5095                             handleButton("ok");
5096                         }else if(opt.buttons.yes){
5097                             handleButton("yes");
5098                         }
5099                     }
5100                 });
5101                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5102                 textareaEl.enableDisplayMode();
5103                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5104                 progressEl.enableDisplayMode();
5105                 
5106                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5107                 var pf = progressEl.dom.firstChild;
5108                 if (pf) {
5109                     pp = Roo.get(pf.firstChild);
5110                     pp.setHeight(pf.offsetHeight);
5111                 }
5112                 
5113             }
5114             return dlg;
5115         },
5116
5117         /**
5118          * Updates the message box body text
5119          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5120          * the XHTML-compliant non-breaking space character '&amp;#160;')
5121          * @return {Roo.MessageBox} This message box
5122          */
5123         updateText : function(text)
5124         {
5125             if(!dlg.isVisible() && !opt.width){
5126                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5127                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5128             }
5129             msgEl.innerHTML = text || '&#160;';
5130       
5131             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5132             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5133             var w = Math.max(
5134                     Math.min(opt.width || cw , this.maxWidth), 
5135                     Math.max(opt.minWidth || this.minWidth, bwidth)
5136             );
5137             if(opt.prompt){
5138                 activeTextEl.setWidth(w);
5139             }
5140             if(dlg.isVisible()){
5141                 dlg.fixedcenter = false;
5142             }
5143             // to big, make it scroll. = But as usual stupid IE does not support
5144             // !important..
5145             
5146             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5147                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5148                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5149             } else {
5150                 bodyEl.dom.style.height = '';
5151                 bodyEl.dom.style.overflowY = '';
5152             }
5153             if (cw > w) {
5154                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5155             } else {
5156                 bodyEl.dom.style.overflowX = '';
5157             }
5158             
5159             dlg.setContentSize(w, bodyEl.getHeight());
5160             if(dlg.isVisible()){
5161                 dlg.fixedcenter = true;
5162             }
5163             return this;
5164         },
5165
5166         /**
5167          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5168          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5169          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5170          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5171          * @return {Roo.MessageBox} This message box
5172          */
5173         updateProgress : function(value, text){
5174             if(text){
5175                 this.updateText(text);
5176             }
5177             
5178             if (pp) { // weird bug on my firefox - for some reason this is not defined
5179                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5180                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5181             }
5182             return this;
5183         },        
5184
5185         /**
5186          * Returns true if the message box is currently displayed
5187          * @return {Boolean} True if the message box is visible, else false
5188          */
5189         isVisible : function(){
5190             return dlg && dlg.isVisible();  
5191         },
5192
5193         /**
5194          * Hides the message box if it is displayed
5195          */
5196         hide : function(){
5197             if(this.isVisible()){
5198                 dlg.hide();
5199             }  
5200         },
5201
5202         /**
5203          * Displays a new message box, or reinitializes an existing message box, based on the config options
5204          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5205          * The following config object properties are supported:
5206          * <pre>
5207 Property    Type             Description
5208 ----------  ---------------  ------------------------------------------------------------------------------------
5209 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5210                                    closes (defaults to undefined)
5211 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5212                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5213 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5214                                    progress and wait dialogs will ignore this property and always hide the
5215                                    close button as they can only be closed programmatically.
5216 cls               String           A custom CSS class to apply to the message box element
5217 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5218                                    displayed (defaults to 75)
5219 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5220                                    function will be btn (the name of the button that was clicked, if applicable,
5221                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5222                                    Progress and wait dialogs will ignore this option since they do not respond to
5223                                    user actions and can only be closed programmatically, so any required function
5224                                    should be called by the same code after it closes the dialog.
5225 icon              String           A CSS class that provides a background image to be used as an icon for
5226                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5227 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5228 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5229 modal             Boolean          False to allow user interaction with the page while the message box is
5230                                    displayed (defaults to true)
5231 msg               String           A string that will replace the existing message box body text (defaults
5232                                    to the XHTML-compliant non-breaking space character '&#160;')
5233 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5234 progress          Boolean          True to display a progress bar (defaults to false)
5235 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5236 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5237 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5238 title             String           The title text
5239 value             String           The string value to set into the active textbox element if displayed
5240 wait              Boolean          True to display a progress bar (defaults to false)
5241 width             Number           The width of the dialog in pixels
5242 </pre>
5243          *
5244          * Example usage:
5245          * <pre><code>
5246 Roo.Msg.show({
5247    title: 'Address',
5248    msg: 'Please enter your address:',
5249    width: 300,
5250    buttons: Roo.MessageBox.OKCANCEL,
5251    multiline: true,
5252    fn: saveAddress,
5253    animEl: 'addAddressBtn'
5254 });
5255 </code></pre>
5256          * @param {Object} config Configuration options
5257          * @return {Roo.MessageBox} This message box
5258          */
5259         show : function(options)
5260         {
5261             
5262             // this causes nightmares if you show one dialog after another
5263             // especially on callbacks..
5264              
5265             if(this.isVisible()){
5266                 
5267                 this.hide();
5268                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5269                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5270                 Roo.log("New Dialog Message:" +  options.msg )
5271                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5272                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5273                 
5274             }
5275             var d = this.getDialog();
5276             opt = options;
5277             d.setTitle(opt.title || "&#160;");
5278             d.closeEl.setDisplayed(opt.closable !== false);
5279             activeTextEl = textboxEl;
5280             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5281             if(opt.prompt){
5282                 if(opt.multiline){
5283                     textboxEl.hide();
5284                     textareaEl.show();
5285                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5286                         opt.multiline : this.defaultTextHeight);
5287                     activeTextEl = textareaEl;
5288                 }else{
5289                     textboxEl.show();
5290                     textareaEl.hide();
5291                 }
5292             }else{
5293                 textboxEl.hide();
5294                 textareaEl.hide();
5295             }
5296             progressEl.setDisplayed(opt.progress === true);
5297             if (opt.progress) {
5298                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5299             }
5300             this.updateProgress(0);
5301             activeTextEl.dom.value = opt.value || "";
5302             if(opt.prompt){
5303                 dlg.setDefaultButton(activeTextEl);
5304             }else{
5305                 var bs = opt.buttons;
5306                 var db = null;
5307                 if(bs && bs.ok){
5308                     db = buttons["ok"];
5309                 }else if(bs && bs.yes){
5310                     db = buttons["yes"];
5311                 }
5312                 dlg.setDefaultButton(db);
5313             }
5314             bwidth = updateButtons(opt.buttons);
5315             this.updateText(opt.msg);
5316             if(opt.cls){
5317                 d.el.addClass(opt.cls);
5318             }
5319             d.proxyDrag = opt.proxyDrag === true;
5320             d.modal = opt.modal !== false;
5321             d.mask = opt.modal !== false ? mask : false;
5322             if(!d.isVisible()){
5323                 // force it to the end of the z-index stack so it gets a cursor in FF
5324                 document.body.appendChild(dlg.el.dom);
5325                 d.animateTarget = null;
5326                 d.show(options.animEl);
5327             }
5328             return this;
5329         },
5330
5331         /**
5332          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5333          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5334          * and closing the message box when the process is complete.
5335          * @param {String} title The title bar text
5336          * @param {String} msg The message box body text
5337          * @return {Roo.MessageBox} This message box
5338          */
5339         progress : function(title, msg){
5340             this.show({
5341                 title : title,
5342                 msg : msg,
5343                 buttons: false,
5344                 progress:true,
5345                 closable:false,
5346                 minWidth: this.minProgressWidth,
5347                 modal : true
5348             });
5349             return this;
5350         },
5351
5352         /**
5353          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5354          * If a callback function is passed it will be called after the user clicks the button, and the
5355          * id of the button that was clicked will be passed as the only parameter to the callback
5356          * (could also be the top-right close button).
5357          * @param {String} title The title bar text
5358          * @param {String} msg The message box body text
5359          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5360          * @param {Object} scope (optional) The scope of the callback function
5361          * @return {Roo.MessageBox} This message box
5362          */
5363         alert : function(title, msg, fn, scope)
5364         {
5365             this.show({
5366                 title : title,
5367                 msg : msg,
5368                 buttons: this.OK,
5369                 fn: fn,
5370                 closable : false,
5371                 scope : scope,
5372                 modal : true
5373             });
5374             return this;
5375         },
5376
5377         /**
5378          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5379          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5380          * You are responsible for closing the message box when the process is complete.
5381          * @param {String} msg The message box body text
5382          * @param {String} title (optional) The title bar text
5383          * @return {Roo.MessageBox} This message box
5384          */
5385         wait : function(msg, title){
5386             this.show({
5387                 title : title,
5388                 msg : msg,
5389                 buttons: false,
5390                 closable:false,
5391                 progress:true,
5392                 modal:true,
5393                 width:300,
5394                 wait:true
5395             });
5396             waitTimer = Roo.TaskMgr.start({
5397                 run: function(i){
5398                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5399                 },
5400                 interval: 1000
5401             });
5402             return this;
5403         },
5404
5405         /**
5406          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5407          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5408          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5409          * @param {String} title The title bar text
5410          * @param {String} msg The message box body text
5411          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5412          * @param {Object} scope (optional) The scope of the callback function
5413          * @return {Roo.MessageBox} This message box
5414          */
5415         confirm : function(title, msg, fn, scope){
5416             this.show({
5417                 title : title,
5418                 msg : msg,
5419                 buttons: this.YESNO,
5420                 fn: fn,
5421                 scope : scope,
5422                 modal : true
5423             });
5424             return this;
5425         },
5426
5427         /**
5428          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5429          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5430          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5431          * (could also be the top-right close button) and the text that was entered will be passed as the two
5432          * parameters to the callback.
5433          * @param {String} title The title bar text
5434          * @param {String} msg The message box body text
5435          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5436          * @param {Object} scope (optional) The scope of the callback function
5437          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5438          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5439          * @return {Roo.MessageBox} This message box
5440          */
5441         prompt : function(title, msg, fn, scope, multiline){
5442             this.show({
5443                 title : title,
5444                 msg : msg,
5445                 buttons: this.OKCANCEL,
5446                 fn: fn,
5447                 minWidth:250,
5448                 scope : scope,
5449                 prompt:true,
5450                 multiline: multiline,
5451                 modal : true
5452             });
5453             return this;
5454         },
5455
5456         /**
5457          * Button config that displays a single OK button
5458          * @type Object
5459          */
5460         OK : {ok:true},
5461         /**
5462          * Button config that displays Yes and No buttons
5463          * @type Object
5464          */
5465         YESNO : {yes:true, no:true},
5466         /**
5467          * Button config that displays OK and Cancel buttons
5468          * @type Object
5469          */
5470         OKCANCEL : {ok:true, cancel:true},
5471         /**
5472          * Button config that displays Yes, No and Cancel buttons
5473          * @type Object
5474          */
5475         YESNOCANCEL : {yes:true, no:true, cancel:true},
5476
5477         /**
5478          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5479          * @type Number
5480          */
5481         defaultTextHeight : 75,
5482         /**
5483          * The maximum width in pixels of the message box (defaults to 600)
5484          * @type Number
5485          */
5486         maxWidth : 600,
5487         /**
5488          * The minimum width in pixels of the message box (defaults to 100)
5489          * @type Number
5490          */
5491         minWidth : 100,
5492         /**
5493          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5494          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5495          * @type Number
5496          */
5497         minProgressWidth : 250,
5498         /**
5499          * An object containing the default button text strings that can be overriden for localized language support.
5500          * Supported properties are: ok, cancel, yes and no.
5501          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5502          * @type Object
5503          */
5504         buttonText : {
5505             ok : "OK",
5506             cancel : "Cancel",
5507             yes : "Yes",
5508             no : "No"
5509         }
5510     };
5511 }();
5512
5513 /**
5514  * Shorthand for {@link Roo.MessageBox}
5515  */
5516 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5517 Roo.Msg = Roo.Msg || Roo.MessageBox;
5518 Roo.bootstrap.nav = {};
5519
5520 /*
5521  * - LGPL
5522  *
5523  * navbar
5524  * 
5525  */
5526
5527 /**
5528  * @class Roo.bootstrap.nav.Bar
5529  * @extends Roo.bootstrap.Component
5530  * @abstract
5531  * Bootstrap Navbar class
5532
5533  * @constructor
5534  * Create a new Navbar
5535  * @param {Object} config The config object
5536  */
5537
5538
5539 Roo.bootstrap.nav.Bar = function(config){
5540     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5541     this.addEvents({
5542         // raw events
5543         /**
5544          * @event beforetoggle
5545          * Fire before toggle the menu
5546          * @param {Roo.EventObject} e
5547          */
5548         "beforetoggle" : true
5549     });
5550 };
5551
5552 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5553     
5554     
5555    
5556     // private
5557     navItems : false,
5558     loadMask : false,
5559     
5560     
5561     getAutoCreate : function(){
5562         
5563         
5564         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5565         
5566     },
5567     
5568     initEvents :function ()
5569     {
5570         //Roo.log(this.el.select('.navbar-toggle',true));
5571         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5572         
5573         var mark = {
5574             tag: "div",
5575             cls:"x-dlg-mask"
5576         };
5577         
5578         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5579         
5580         var size = this.el.getSize();
5581         this.maskEl.setSize(size.width, size.height);
5582         this.maskEl.enableDisplayMode("block");
5583         this.maskEl.hide();
5584         
5585         if(this.loadMask){
5586             this.maskEl.show();
5587         }
5588     },
5589     
5590     
5591     getChildContainer : function()
5592     {
5593         if (this.el && this.el.select('.collapse').getCount()) {
5594             return this.el.select('.collapse',true).first();
5595         }
5596         
5597         return this.el;
5598     },
5599     
5600     mask : function()
5601     {
5602         this.maskEl.show();
5603     },
5604     
5605     unmask : function()
5606     {
5607         this.maskEl.hide();
5608     },
5609     onToggle : function()
5610     {
5611         
5612         if(this.fireEvent('beforetoggle', this) === false){
5613             return;
5614         }
5615         var ce = this.el.select('.navbar-collapse',true).first();
5616       
5617         if (!ce.hasClass('show')) {
5618            this.expand();
5619         } else {
5620             this.collapse();
5621         }
5622         
5623         
5624     
5625     },
5626     /**
5627      * Expand the navbar pulldown 
5628      */
5629     expand : function ()
5630     {
5631        
5632         var ce = this.el.select('.navbar-collapse',true).first();
5633         if (ce.hasClass('collapsing')) {
5634             return;
5635         }
5636         ce.dom.style.height = '';
5637                // show it...
5638         ce.addClass('in'); // old...
5639         ce.removeClass('collapse');
5640         ce.addClass('show');
5641         var h = ce.getHeight();
5642         Roo.log(h);
5643         ce.removeClass('show');
5644         // at this point we should be able to see it..
5645         ce.addClass('collapsing');
5646         
5647         ce.setHeight(0); // resize it ...
5648         ce.on('transitionend', function() {
5649             //Roo.log('done transition');
5650             ce.removeClass('collapsing');
5651             ce.addClass('show');
5652             ce.removeClass('collapse');
5653
5654             ce.dom.style.height = '';
5655         }, this, { single: true} );
5656         ce.setHeight(h);
5657         ce.dom.scrollTop = 0;
5658     },
5659     /**
5660      * Collapse the navbar pulldown 
5661      */
5662     collapse : function()
5663     {
5664          var ce = this.el.select('.navbar-collapse',true).first();
5665        
5666         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5667             // it's collapsed or collapsing..
5668             return;
5669         }
5670         ce.removeClass('in'); // old...
5671         ce.setHeight(ce.getHeight());
5672         ce.removeClass('show');
5673         ce.addClass('collapsing');
5674         
5675         ce.on('transitionend', function() {
5676             ce.dom.style.height = '';
5677             ce.removeClass('collapsing');
5678             ce.addClass('collapse');
5679         }, this, { single: true} );
5680         ce.setHeight(0);
5681     }
5682     
5683     
5684     
5685 });
5686
5687
5688
5689  
5690
5691  /*
5692  * - LGPL
5693  *
5694  * navbar
5695  * 
5696  */
5697
5698 /**
5699  * @class Roo.bootstrap.nav.Simplebar
5700  * @extends Roo.bootstrap.nav.Bar
5701  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5702  * Bootstrap Sidebar class
5703  *
5704  * @cfg {Boolean} inverse is inverted color
5705  * 
5706  * @cfg {String} type (nav | pills | tabs)
5707  * @cfg {Boolean} arrangement stacked | justified
5708  * @cfg {String} align (left | right) alignment
5709  * 
5710  * @cfg {Boolean} main (true|false) main nav bar? default false
5711  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5712  * 
5713  * @cfg {String} tag (header|footer|nav|div) default is nav 
5714
5715  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5716  * 
5717  * 
5718  * @constructor
5719  * Create a new Sidebar
5720  * @param {Object} config The config object
5721  */
5722
5723
5724 Roo.bootstrap.nav.Simplebar = function(config){
5725     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5726 };
5727
5728 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5729     
5730     inverse: false,
5731     
5732     type: false,
5733     arrangement: '',
5734     align : false,
5735     
5736     weight : 'light',
5737     
5738     main : false,
5739     
5740     
5741     tag : false,
5742     
5743     
5744     getAutoCreate : function(){
5745         
5746         
5747         var cfg = {
5748             tag : this.tag || 'div',
5749             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5750         };
5751         if (['light','white'].indexOf(this.weight) > -1) {
5752             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5753         }
5754         cfg.cls += ' bg-' + this.weight;
5755         
5756         if (this.inverse) {
5757             cfg.cls += ' navbar-inverse';
5758             
5759         }
5760         
5761         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5762         
5763         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5764             return cfg;
5765         }
5766         
5767         
5768     
5769         
5770         cfg.cn = [
5771             {
5772                 cls: 'nav nav-' + this.xtype,
5773                 tag : 'ul'
5774             }
5775         ];
5776         
5777          
5778         this.type = this.type || 'nav';
5779         if (['tabs','pills'].indexOf(this.type) != -1) {
5780             cfg.cn[0].cls += ' nav-' + this.type
5781         
5782         
5783         } else {
5784             if (this.type!=='nav') {
5785                 Roo.log('nav type must be nav/tabs/pills')
5786             }
5787             cfg.cn[0].cls += ' navbar-nav'
5788         }
5789         
5790         
5791         
5792         
5793         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5794             cfg.cn[0].cls += ' nav-' + this.arrangement;
5795         }
5796         
5797         
5798         if (this.align === 'right') {
5799             cfg.cn[0].cls += ' navbar-right';
5800         }
5801         
5802         
5803         
5804         
5805         return cfg;
5806     
5807         
5808     }
5809     
5810     
5811     
5812 });
5813
5814
5815
5816  
5817
5818  
5819        /*
5820  * - LGPL
5821  *
5822  * navbar
5823  * navbar-fixed-top
5824  * navbar-expand-md  fixed-top 
5825  */
5826
5827 /**
5828  * @class Roo.bootstrap.nav.Headerbar
5829  * @extends Roo.bootstrap.nav.Simplebar
5830  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5831  * Bootstrap Sidebar class
5832  *
5833  * @cfg {String} brand what is brand
5834  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5835  * @cfg {String} brand_href href of the brand
5836  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5837  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5838  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5839  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5840  * 
5841  * @constructor
5842  * Create a new Sidebar
5843  * @param {Object} config The config object
5844  */
5845
5846
5847 Roo.bootstrap.nav.Headerbar = function(config){
5848     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5849       
5850 };
5851
5852 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5853     
5854     position: '',
5855     brand: '',
5856     brand_href: false,
5857     srButton : true,
5858     autohide : false,
5859     desktopCenter : false,
5860    
5861     
5862     getAutoCreate : function(){
5863         
5864         var   cfg = {
5865             tag: this.nav || 'nav',
5866             cls: 'navbar navbar-expand-md',
5867             role: 'navigation',
5868             cn: []
5869         };
5870         
5871         var cn = cfg.cn;
5872         if (this.desktopCenter) {
5873             cn.push({cls : 'container', cn : []});
5874             cn = cn[0].cn;
5875         }
5876         
5877         if(this.srButton){
5878             var btn = {
5879                 tag: 'button',
5880                 type: 'button',
5881                 cls: 'navbar-toggle navbar-toggler',
5882                 'data-toggle': 'collapse',
5883                 cn: [
5884                     {
5885                         tag: 'span',
5886                         cls: 'sr-only',
5887                         html: 'Toggle navigation'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar navbar-toggler-icon'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     },
5897                     {
5898                         tag: 'span',
5899                         cls: 'icon-bar'
5900                     }
5901                 ]
5902             };
5903             
5904             cn.push( Roo.bootstrap.version == 4 ? btn : {
5905                 tag: 'div',
5906                 cls: 'navbar-header',
5907                 cn: [
5908                     btn
5909                 ]
5910             });
5911         }
5912         
5913         cn.push({
5914             tag: 'div',
5915             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5916             cn : []
5917         });
5918         
5919         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5920         
5921         if (['light','white'].indexOf(this.weight) > -1) {
5922             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5923         }
5924         cfg.cls += ' bg-' + this.weight;
5925         
5926         
5927         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5928             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5929             
5930             // tag can override this..
5931             
5932             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5933         }
5934         
5935         if (this.brand !== '') {
5936             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5937             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5938                 tag: 'a',
5939                 href: this.brand_href ? this.brand_href : '#',
5940                 cls: 'navbar-brand',
5941                 cn: [
5942                 this.brand
5943                 ]
5944             });
5945         }
5946         
5947         if(this.main){
5948             cfg.cls += ' main-nav';
5949         }
5950         
5951         
5952         return cfg;
5953
5954         
5955     },
5956     getHeaderChildContainer : function()
5957     {
5958         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5959             return this.el.select('.navbar-header',true).first();
5960         }
5961         
5962         return this.getChildContainer();
5963     },
5964     
5965     getChildContainer : function()
5966     {
5967          
5968         return this.el.select('.roo-navbar-collapse',true).first();
5969          
5970         
5971     },
5972     
5973     initEvents : function()
5974     {
5975         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5976         
5977         if (this.autohide) {
5978             
5979             var prevScroll = 0;
5980             var ft = this.el;
5981             
5982             Roo.get(document).on('scroll',function(e) {
5983                 var ns = Roo.get(document).getScroll().top;
5984                 var os = prevScroll;
5985                 prevScroll = ns;
5986                 
5987                 if(ns > os){
5988                     ft.removeClass('slideDown');
5989                     ft.addClass('slideUp');
5990                     return;
5991                 }
5992                 ft.removeClass('slideUp');
5993                 ft.addClass('slideDown');
5994                  
5995               
5996           },this);
5997         }
5998     }    
5999     
6000 });
6001
6002
6003
6004  
6005
6006  /*
6007  * - LGPL
6008  *
6009  * navbar
6010  * 
6011  */
6012
6013 /**
6014  * @class Roo.bootstrap.nav.Sidebar
6015  * @extends Roo.bootstrap.Navbar
6016  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6017  * Bootstrap Sidebar class
6018  * 
6019  * @constructor
6020  * Create a new Sidebar
6021  * @param {Object} config The config object
6022  */
6023
6024
6025 Roo.bootstrap.nav.Sidebar = function(config){
6026     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6027 };
6028
6029 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6030     
6031     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6032     
6033     getAutoCreate : function(){
6034         
6035         
6036         return  {
6037             tag: 'div',
6038             cls: 'sidebar sidebar-nav'
6039         };
6040     
6041         
6042     }
6043     
6044     
6045     
6046 });
6047
6048
6049
6050  
6051
6052  /*
6053  * - LGPL
6054  *
6055  * nav group
6056  * 
6057  */
6058
6059 /**
6060  * @class Roo.bootstrap.nav.Group
6061  * @extends Roo.bootstrap.Component
6062  * @children Roo.bootstrap.nav.Item
6063  * Bootstrap NavGroup class
6064  * @cfg {String} align (left|right)
6065  * @cfg {Boolean} inverse
6066  * @cfg {String} type (nav|pills|tab) default nav
6067  * @cfg {String} navId - reference Id for navbar.
6068  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6069  * 
6070  * @constructor
6071  * Create a new nav group
6072  * @param {Object} config The config object
6073  */
6074
6075 Roo.bootstrap.nav.Group = function(config){
6076     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6077     this.navItems = [];
6078    
6079     Roo.bootstrap.nav.Group.register(this);
6080      this.addEvents({
6081         /**
6082              * @event changed
6083              * Fires when the active item changes
6084              * @param {Roo.bootstrap.nav.Group} this
6085              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6086              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6087          */
6088         'changed': true
6089      });
6090     
6091 };
6092
6093 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6094     
6095     align: '',
6096     inverse: false,
6097     form: false,
6098     type: 'nav',
6099     navId : '',
6100     // private
6101     pilltype : true,
6102     
6103     navItems : false, 
6104     
6105     getAutoCreate : function()
6106     {
6107         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6108         
6109         cfg = {
6110             tag : 'ul',
6111             cls: 'nav' 
6112         };
6113         if (Roo.bootstrap.version == 4) {
6114             if (['tabs','pills'].indexOf(this.type) != -1) {
6115                 cfg.cls += ' nav-' + this.type; 
6116             } else {
6117                 // trying to remove so header bar can right align top?
6118                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6119                     // do not use on header bar... 
6120                     cfg.cls += ' navbar-nav';
6121                 }
6122             }
6123             
6124         } else {
6125             if (['tabs','pills'].indexOf(this.type) != -1) {
6126                 cfg.cls += ' nav-' + this.type
6127             } else {
6128                 if (this.type !== 'nav') {
6129                     Roo.log('nav type must be nav/tabs/pills')
6130                 }
6131                 cfg.cls += ' navbar-nav'
6132             }
6133         }
6134         
6135         if (this.parent() && this.parent().sidebar) {
6136             cfg = {
6137                 tag: 'ul',
6138                 cls: 'dashboard-menu sidebar-menu'
6139             };
6140             
6141             return cfg;
6142         }
6143         
6144         if (this.form === true) {
6145             cfg = {
6146                 tag: 'form',
6147                 cls: 'navbar-form form-inline'
6148             };
6149             //nav navbar-right ml-md-auto
6150             if (this.align === 'right') {
6151                 cfg.cls += ' navbar-right ml-md-auto';
6152             } else {
6153                 cfg.cls += ' navbar-left';
6154             }
6155         }
6156         
6157         if (this.align === 'right') {
6158             cfg.cls += ' navbar-right ml-md-auto';
6159         } else {
6160             cfg.cls += ' mr-auto';
6161         }
6162         
6163         if (this.inverse) {
6164             cfg.cls += ' navbar-inverse';
6165             
6166         }
6167         
6168         
6169         return cfg;
6170     },
6171     /**
6172     * sets the active Navigation item
6173     * @param {Roo.bootstrap.nav.Item} the new current navitem
6174     */
6175     setActiveItem : function(item)
6176     {
6177         var prev = false;
6178         Roo.each(this.navItems, function(v){
6179             if (v == item) {
6180                 return ;
6181             }
6182             if (v.isActive()) {
6183                 v.setActive(false, true);
6184                 prev = v;
6185                 
6186             }
6187             
6188         });
6189
6190         item.setActive(true, true);
6191         this.fireEvent('changed', this, item, prev);
6192         
6193         
6194     },
6195     /**
6196     * gets the active Navigation item
6197     * @return {Roo.bootstrap.nav.Item} the current navitem
6198     */
6199     getActive : function()
6200     {
6201         
6202         var prev = false;
6203         Roo.each(this.navItems, function(v){
6204             
6205             if (v.isActive()) {
6206                 prev = v;
6207                 
6208             }
6209             
6210         });
6211         return prev;
6212     },
6213     
6214     indexOfNav : function()
6215     {
6216         
6217         var prev = false;
6218         Roo.each(this.navItems, function(v,i){
6219             
6220             if (v.isActive()) {
6221                 prev = i;
6222                 
6223             }
6224             
6225         });
6226         return prev;
6227     },
6228     /**
6229     * adds a Navigation item
6230     * @param {Roo.bootstrap.nav.Item} the navitem to add
6231     */
6232     addItem : function(cfg)
6233     {
6234         if (this.form && Roo.bootstrap.version == 4) {
6235             cfg.tag = 'div';
6236         }
6237         var cn = new Roo.bootstrap.nav.Item(cfg);
6238         this.register(cn);
6239         cn.parentId = this.id;
6240         cn.onRender(this.el, null);
6241         return cn;
6242     },
6243     /**
6244     * register a Navigation item
6245     * @param {Roo.bootstrap.nav.Item} the navitem to add
6246     */
6247     register : function(item)
6248     {
6249         this.navItems.push( item);
6250         item.navId = this.navId;
6251     
6252     },
6253     
6254     /**
6255     * clear all the Navigation item
6256     */
6257    
6258     clearAll : function()
6259     {
6260         this.navItems = [];
6261         this.el.dom.innerHTML = '';
6262     },
6263     
6264     getNavItem: function(tabId)
6265     {
6266         var ret = false;
6267         Roo.each(this.navItems, function(e) {
6268             if (e.tabId == tabId) {
6269                ret =  e;
6270                return false;
6271             }
6272             return true;
6273             
6274         });
6275         return ret;
6276     },
6277     
6278     setActiveNext : function()
6279     {
6280         var i = this.indexOfNav(this.getActive());
6281         if (i > this.navItems.length) {
6282             return;
6283         }
6284         this.setActiveItem(this.navItems[i+1]);
6285     },
6286     setActivePrev : function()
6287     {
6288         var i = this.indexOfNav(this.getActive());
6289         if (i  < 1) {
6290             return;
6291         }
6292         this.setActiveItem(this.navItems[i-1]);
6293     },
6294     clearWasActive : function(except) {
6295         Roo.each(this.navItems, function(e) {
6296             if (e.tabId != except.tabId && e.was_active) {
6297                e.was_active = false;
6298                return false;
6299             }
6300             return true;
6301             
6302         });
6303     },
6304     getWasActive : function ()
6305     {
6306         var r = false;
6307         Roo.each(this.navItems, function(e) {
6308             if (e.was_active) {
6309                r = e;
6310                return false;
6311             }
6312             return true;
6313             
6314         });
6315         return r;
6316     }
6317     
6318     
6319 });
6320
6321  
6322 Roo.apply(Roo.bootstrap.nav.Group, {
6323     
6324     groups: {},
6325      /**
6326     * register a Navigation Group
6327     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6328     */
6329     register : function(navgrp)
6330     {
6331         this.groups[navgrp.navId] = navgrp;
6332         
6333     },
6334     /**
6335     * fetch a Navigation Group based on the navigation ID
6336     * @param {string} the navgroup to add
6337     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6338     */
6339     get: function(navId) {
6340         if (typeof(this.groups[navId]) == 'undefined') {
6341             return false;
6342             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6343         }
6344         return this.groups[navId] ;
6345     }
6346     
6347     
6348     
6349 });
6350
6351  /**
6352  * @class Roo.bootstrap.nav.Item
6353  * @extends Roo.bootstrap.Component
6354  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6355  * @licence LGPL
6356  * Bootstrap Navbar.NavItem class
6357  * 
6358  * @cfg {String} href  link to
6359  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6360  * @cfg {Boolean} button_outline show and outlined button
6361  * @cfg {String} html content of button
6362  * @cfg {String} badge text inside badge
6363  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6364  * @cfg {String} glyphicon DEPRICATED - use fa
6365  * @cfg {String} icon DEPRICATED - use fa
6366  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6367  * @cfg {Boolean} active Is item active
6368  * @cfg {Boolean} disabled Is item disabled
6369  * @cfg {String} linkcls  Link Class
6370  * @cfg {Boolean} preventDefault (true | false) default false
6371  * @cfg {String} tabId the tab that this item activates.
6372  * @cfg {String} tagtype (a|span) render as a href or span?
6373  * @cfg {Boolean} animateRef (true|false) link to element default false  
6374  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6375   
6376  * @constructor
6377  * Create a new Navbar Item
6378  * @param {Object} config The config object
6379  */
6380 Roo.bootstrap.nav.Item = function(config){
6381     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6382     this.addEvents({
6383         // raw events
6384         /**
6385          * @event click
6386          * The raw click event for the entire grid.
6387          * @param {Roo.EventObject} e
6388          */
6389         "click" : true,
6390          /**
6391             * @event changed
6392             * Fires when the active item active state changes
6393             * @param {Roo.bootstrap.nav.Item} this
6394             * @param {boolean} state the new state
6395              
6396          */
6397         'changed': true,
6398         /**
6399             * @event scrollto
6400             * Fires when scroll to element
6401             * @param {Roo.bootstrap.nav.Item} this
6402             * @param {Object} options
6403             * @param {Roo.EventObject} e
6404              
6405          */
6406         'scrollto': true
6407     });
6408    
6409 };
6410
6411 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6412     
6413     href: false,
6414     html: '',
6415     badge: '',
6416     icon: false,
6417     fa : false,
6418     glyphicon: false,
6419     active: false,
6420     preventDefault : false,
6421     tabId : false,
6422     tagtype : 'a',
6423     tag: 'li',
6424     disabled : false,
6425     animateRef : false,
6426     was_active : false,
6427     button_weight : '',
6428     button_outline : false,
6429     linkcls : '',
6430     navLink: false,
6431     
6432     getAutoCreate : function(){
6433          
6434         var cfg = {
6435             tag: this.tag,
6436             cls: 'nav-item'
6437         };
6438         
6439         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6440         
6441         if (this.active) {
6442             cfg.cls +=  ' active' ;
6443         }
6444         if (this.disabled) {
6445             cfg.cls += ' disabled';
6446         }
6447         
6448         // BS4 only?
6449         if (this.button_weight.length) {
6450             cfg.tag = this.href ? 'a' : 'button';
6451             cfg.html = this.html || '';
6452             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6453             if (this.href) {
6454                 cfg.href = this.href;
6455             }
6456             if (this.fa) {
6457                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6458             } else {
6459                 cfg.cls += " nav-html";
6460             }
6461             
6462             // menu .. should add dropdown-menu class - so no need for carat..
6463             
6464             if (this.badge !== '') {
6465                  
6466                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6467             }
6468             return cfg;
6469         }
6470         
6471         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6472             cfg.cn = [
6473                 {
6474                     tag: this.tagtype,
6475                     href : this.href || "#",
6476                     html: this.html || '',
6477                     cls : ''
6478                 }
6479             ];
6480             if (this.tagtype == 'a') {
6481                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6482         
6483             }
6484             if (this.icon) {
6485                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6486             } else  if (this.fa) {
6487                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6488             } else if(this.glyphicon) {
6489                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6490             } else {
6491                 cfg.cn[0].cls += " nav-html";
6492             }
6493             
6494             if (this.menu) {
6495                 cfg.cn[0].html += " <span class='caret'></span>";
6496              
6497             }
6498             
6499             if (this.badge !== '') {
6500                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6501             }
6502         }
6503         
6504         
6505         
6506         return cfg;
6507     },
6508     onRender : function(ct, position)
6509     {
6510        // Roo.log("Call onRender: " + this.xtype);
6511         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6512             this.tag = 'div';
6513         }
6514         
6515         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6516         this.navLink = this.el.select('.nav-link',true).first();
6517         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6518         return ret;
6519     },
6520       
6521     
6522     initEvents: function() 
6523     {
6524         if (typeof (this.menu) != 'undefined') {
6525             this.menu.parentType = this.xtype;
6526             this.menu.triggerEl = this.el;
6527             this.menu = this.addxtype(Roo.apply({}, this.menu));
6528         }
6529         
6530         this.el.on('click', this.onClick, this);
6531         
6532         //if(this.tagtype == 'span'){
6533         //    this.el.select('span',true).on('click', this.onClick, this);
6534         //}
6535        
6536         // at this point parent should be available..
6537         this.parent().register(this);
6538     },
6539     
6540     onClick : function(e)
6541     {
6542         if (e.getTarget('.dropdown-menu-item')) {
6543             // did you click on a menu itemm.... - then don't trigger onclick..
6544             return;
6545         }
6546         
6547         if(
6548                 this.preventDefault || 
6549                 this.href == '#' 
6550         ){
6551             Roo.log("NavItem - prevent Default?");
6552             e.preventDefault();
6553         }
6554         
6555         if (this.disabled) {
6556             return;
6557         }
6558         
6559         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6560         if (tg && tg.transition) {
6561             Roo.log("waiting for the transitionend");
6562             return;
6563         }
6564         
6565         
6566         
6567         //Roo.log("fire event clicked");
6568         if(this.fireEvent('click', this, e) === false){
6569             return;
6570         };
6571         
6572         if(this.tagtype == 'span'){
6573             return;
6574         }
6575         
6576         //Roo.log(this.href);
6577         var ael = this.el.select('a',true).first();
6578         //Roo.log(ael);
6579         
6580         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6581             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6582             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6583                 return; // ignore... - it's a 'hash' to another page.
6584             }
6585             Roo.log("NavItem - prevent Default?");
6586             e.preventDefault();
6587             this.scrollToElement(e);
6588         }
6589         
6590         
6591         var p =  this.parent();
6592    
6593         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6594             if (typeof(p.setActiveItem) !== 'undefined') {
6595                 p.setActiveItem(this);
6596             }
6597         }
6598         
6599         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6600         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6601             // remove the collapsed menu expand...
6602             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6603         }
6604     },
6605     
6606     isActive: function () {
6607         return this.active
6608     },
6609     setActive : function(state, fire, is_was_active)
6610     {
6611         if (this.active && !state && this.navId) {
6612             this.was_active = true;
6613             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6614             if (nv) {
6615                 nv.clearWasActive(this);
6616             }
6617             
6618         }
6619         this.active = state;
6620         
6621         if (!state ) {
6622             this.el.removeClass('active');
6623             this.navLink ? this.navLink.removeClass('active') : false;
6624         } else if (!this.el.hasClass('active')) {
6625             
6626             this.el.addClass('active');
6627             if (Roo.bootstrap.version == 4 && this.navLink ) {
6628                 this.navLink.addClass('active');
6629             }
6630             
6631         }
6632         if (fire) {
6633             this.fireEvent('changed', this, state);
6634         }
6635         
6636         // show a panel if it's registered and related..
6637         
6638         if (!this.navId || !this.tabId || !state || is_was_active) {
6639             return;
6640         }
6641         
6642         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6643         if (!tg) {
6644             return;
6645         }
6646         var pan = tg.getPanelByName(this.tabId);
6647         if (!pan) {
6648             return;
6649         }
6650         // if we can not flip to new panel - go back to old nav highlight..
6651         if (false == tg.showPanel(pan)) {
6652             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6653             if (nv) {
6654                 var onav = nv.getWasActive();
6655                 if (onav) {
6656                     onav.setActive(true, false, true);
6657                 }
6658             }
6659             
6660         }
6661         
6662         
6663         
6664     },
6665      // this should not be here...
6666     setDisabled : function(state)
6667     {
6668         this.disabled = state;
6669         if (!state ) {
6670             this.el.removeClass('disabled');
6671         } else if (!this.el.hasClass('disabled')) {
6672             this.el.addClass('disabled');
6673         }
6674         
6675     },
6676     
6677     /**
6678      * Fetch the element to display the tooltip on.
6679      * @return {Roo.Element} defaults to this.el
6680      */
6681     tooltipEl : function()
6682     {
6683         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6684     },
6685     
6686     scrollToElement : function(e)
6687     {
6688         var c = document.body;
6689         
6690         /*
6691          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6692          */
6693         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6694             c = document.documentElement;
6695         }
6696         
6697         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6698         
6699         if(!target){
6700             return;
6701         }
6702
6703         var o = target.calcOffsetsTo(c);
6704         
6705         var options = {
6706             target : target,
6707             value : o[1]
6708         };
6709         
6710         this.fireEvent('scrollto', this, options, e);
6711         
6712         Roo.get(c).scrollTo('top', options.value, true);
6713         
6714         return;
6715     },
6716     /**
6717      * Set the HTML (text content) of the item
6718      * @param {string} html  content for the nav item
6719      */
6720     setHtml : function(html)
6721     {
6722         this.html = html;
6723         this.htmlEl.dom.innerHTML = html;
6724         
6725     } 
6726 });
6727  
6728
6729  /*
6730  * - LGPL
6731  *
6732  * sidebar item
6733  *
6734  *  li
6735  *    <span> icon </span>
6736  *    <span> text </span>
6737  *    <span>badge </span>
6738  */
6739
6740 /**
6741  * @class Roo.bootstrap.nav.SidebarItem
6742  * @extends Roo.bootstrap.nav.Item
6743  * Bootstrap Navbar.NavSidebarItem class
6744  * 
6745  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6746  * {Boolean} open is the menu open
6747  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6748  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6749  * {String} buttonSize (sm|md|lg)the extra classes for the button
6750  * {Boolean} showArrow show arrow next to the text (default true)
6751  * @constructor
6752  * Create a new Navbar Button
6753  * @param {Object} config The config object
6754  */
6755 Roo.bootstrap.nav.SidebarItem = function(config){
6756     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6757     this.addEvents({
6758         // raw events
6759         /**
6760          * @event click
6761          * The raw click event for the entire grid.
6762          * @param {Roo.EventObject} e
6763          */
6764         "click" : true,
6765          /**
6766             * @event changed
6767             * Fires when the active item active state changes
6768             * @param {Roo.bootstrap.nav.SidebarItem} this
6769             * @param {boolean} state the new state
6770              
6771          */
6772         'changed': true
6773     });
6774    
6775 };
6776
6777 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6778     
6779     badgeWeight : 'default',
6780     
6781     open: false,
6782     
6783     buttonView : false,
6784     
6785     buttonWeight : 'default',
6786     
6787     buttonSize : 'md',
6788     
6789     showArrow : true,
6790     
6791     getAutoCreate : function(){
6792         
6793         
6794         var a = {
6795                 tag: 'a',
6796                 href : this.href || '#',
6797                 cls: '',
6798                 html : '',
6799                 cn : []
6800         };
6801         
6802         if(this.buttonView){
6803             a = {
6804                 tag: 'button',
6805                 href : this.href || '#',
6806                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6807                 html : this.html,
6808                 cn : []
6809             };
6810         }
6811         
6812         var cfg = {
6813             tag: 'li',
6814             cls: '',
6815             cn: [ a ]
6816         };
6817         
6818         if (this.active) {
6819             cfg.cls += ' active';
6820         }
6821         
6822         if (this.disabled) {
6823             cfg.cls += ' disabled';
6824         }
6825         if (this.open) {
6826             cfg.cls += ' open x-open';
6827         }
6828         // left icon..
6829         if (this.glyphicon || this.icon) {
6830             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6831             a.cn.push({ tag : 'i', cls : c }) ;
6832         }
6833         
6834         if(!this.buttonView){
6835             var span = {
6836                 tag: 'span',
6837                 html : this.html || ''
6838             };
6839
6840             a.cn.push(span);
6841             
6842         }
6843         
6844         if (this.badge !== '') {
6845             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6846         }
6847         
6848         if (this.menu) {
6849             
6850             if(this.showArrow){
6851                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6852             }
6853             
6854             a.cls += ' dropdown-toggle treeview' ;
6855         }
6856         
6857         return cfg;
6858     },
6859     
6860     initEvents : function()
6861     { 
6862         if (typeof (this.menu) != 'undefined') {
6863             this.menu.parentType = this.xtype;
6864             this.menu.triggerEl = this.el;
6865             this.menu = this.addxtype(Roo.apply({}, this.menu));
6866         }
6867         
6868         this.el.on('click', this.onClick, this);
6869         
6870         if(this.badge !== ''){
6871             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6872         }
6873         
6874     },
6875     
6876     onClick : function(e)
6877     {
6878         if(this.disabled){
6879             e.preventDefault();
6880             return;
6881         }
6882         
6883         if(this.preventDefault){
6884             e.preventDefault();
6885         }
6886         
6887         this.fireEvent('click', this, e);
6888     },
6889     
6890     disable : function()
6891     {
6892         this.setDisabled(true);
6893     },
6894     
6895     enable : function()
6896     {
6897         this.setDisabled(false);
6898     },
6899     
6900     setDisabled : function(state)
6901     {
6902         if(this.disabled == state){
6903             return;
6904         }
6905         
6906         this.disabled = state;
6907         
6908         if (state) {
6909             this.el.addClass('disabled');
6910             return;
6911         }
6912         
6913         this.el.removeClass('disabled');
6914         
6915         return;
6916     },
6917     
6918     setActive : function(state)
6919     {
6920         if(this.active == state){
6921             return;
6922         }
6923         
6924         this.active = state;
6925         
6926         if (state) {
6927             this.el.addClass('active');
6928             return;
6929         }
6930         
6931         this.el.removeClass('active');
6932         
6933         return;
6934     },
6935     
6936     isActive: function () 
6937     {
6938         return this.active;
6939     },
6940     
6941     setBadge : function(str)
6942     {
6943         if(!this.badgeEl){
6944             return;
6945         }
6946         
6947         this.badgeEl.dom.innerHTML = str;
6948     }
6949     
6950    
6951      
6952  
6953 });
6954  
6955
6956  /*
6957  * - LGPL
6958  *
6959  * nav progress bar
6960  * 
6961  */
6962
6963 /**
6964  * @class Roo.bootstrap.nav.ProgressBar
6965  * @extends Roo.bootstrap.Component
6966  * @children Roo.bootstrap.nav.ProgressBarItem
6967  * Bootstrap NavProgressBar class
6968  * 
6969  * @constructor
6970  * Create a new nav progress bar - a bar indicating step along a process
6971  * @param {Object} config The config object
6972  */
6973
6974 Roo.bootstrap.nav.ProgressBar = function(config){
6975     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6976
6977     this.bullets = this.bullets || [];
6978    
6979 //    Roo.bootstrap.nav.ProgressBar.register(this);
6980      this.addEvents({
6981         /**
6982              * @event changed
6983              * Fires when the active item changes
6984              * @param {Roo.bootstrap.nav.ProgressBar} this
6985              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6986              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6987          */
6988         'changed': true
6989      });
6990     
6991 };
6992
6993 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6994     /**
6995      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6996      * Bullets for the Nav Progress bar for the toolbar
6997      */
6998     bullets : [],
6999     barItems : [],
7000     
7001     getAutoCreate : function()
7002     {
7003         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7004         
7005         cfg = {
7006             tag : 'div',
7007             cls : 'roo-navigation-bar-group',
7008             cn : [
7009                 {
7010                     tag : 'div',
7011                     cls : 'roo-navigation-top-bar'
7012                 },
7013                 {
7014                     tag : 'div',
7015                     cls : 'roo-navigation-bullets-bar',
7016                     cn : [
7017                         {
7018                             tag : 'ul',
7019                             cls : 'roo-navigation-bar'
7020                         }
7021                     ]
7022                 },
7023                 
7024                 {
7025                     tag : 'div',
7026                     cls : 'roo-navigation-bottom-bar'
7027                 }
7028             ]
7029             
7030         };
7031         
7032         return cfg;
7033         
7034     },
7035     
7036     initEvents: function() 
7037     {
7038         
7039     },
7040     
7041     onRender : function(ct, position) 
7042     {
7043         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7044         
7045         if(this.bullets.length){
7046             Roo.each(this.bullets, function(b){
7047                this.addItem(b);
7048             }, this);
7049         }
7050         
7051         this.format();
7052         
7053     },
7054     
7055     addItem : function(cfg)
7056     {
7057         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7058         
7059         item.parentId = this.id;
7060         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7061         
7062         if(cfg.html){
7063             var top = new Roo.bootstrap.Element({
7064                 tag : 'div',
7065                 cls : 'roo-navigation-bar-text'
7066             });
7067             
7068             var bottom = new Roo.bootstrap.Element({
7069                 tag : 'div',
7070                 cls : 'roo-navigation-bar-text'
7071             });
7072             
7073             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7074             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7075             
7076             var topText = new Roo.bootstrap.Element({
7077                 tag : 'span',
7078                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7079             });
7080             
7081             var bottomText = new Roo.bootstrap.Element({
7082                 tag : 'span',
7083                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7084             });
7085             
7086             topText.onRender(top.el, null);
7087             bottomText.onRender(bottom.el, null);
7088             
7089             item.topEl = top;
7090             item.bottomEl = bottom;
7091         }
7092         
7093         this.barItems.push(item);
7094         
7095         return item;
7096     },
7097     
7098     getActive : function()
7099     {
7100         var active = false;
7101         
7102         Roo.each(this.barItems, function(v){
7103             
7104             if (!v.isActive()) {
7105                 return;
7106             }
7107             
7108             active = v;
7109             return false;
7110             
7111         });
7112         
7113         return active;
7114     },
7115     
7116     setActiveItem : function(item)
7117     {
7118         var prev = false;
7119         
7120         Roo.each(this.barItems, function(v){
7121             if (v.rid == item.rid) {
7122                 return ;
7123             }
7124             
7125             if (v.isActive()) {
7126                 v.setActive(false);
7127                 prev = v;
7128             }
7129         });
7130
7131         item.setActive(true);
7132         
7133         this.fireEvent('changed', this, item, prev);
7134     },
7135     
7136     getBarItem: function(rid)
7137     {
7138         var ret = false;
7139         
7140         Roo.each(this.barItems, function(e) {
7141             if (e.rid != rid) {
7142                 return;
7143             }
7144             
7145             ret =  e;
7146             return false;
7147         });
7148         
7149         return ret;
7150     },
7151     
7152     indexOfItem : function(item)
7153     {
7154         var index = false;
7155         
7156         Roo.each(this.barItems, function(v, i){
7157             
7158             if (v.rid != item.rid) {
7159                 return;
7160             }
7161             
7162             index = i;
7163             return false
7164         });
7165         
7166         return index;
7167     },
7168     
7169     setActiveNext : function()
7170     {
7171         var i = this.indexOfItem(this.getActive());
7172         
7173         if (i > this.barItems.length) {
7174             return;
7175         }
7176         
7177         this.setActiveItem(this.barItems[i+1]);
7178     },
7179     
7180     setActivePrev : function()
7181     {
7182         var i = this.indexOfItem(this.getActive());
7183         
7184         if (i  < 1) {
7185             return;
7186         }
7187         
7188         this.setActiveItem(this.barItems[i-1]);
7189     },
7190     
7191     format : function()
7192     {
7193         if(!this.barItems.length){
7194             return;
7195         }
7196      
7197         var width = 100 / this.barItems.length;
7198         
7199         Roo.each(this.barItems, function(i){
7200             i.el.setStyle('width', width + '%');
7201             i.topEl.el.setStyle('width', width + '%');
7202             i.bottomEl.el.setStyle('width', width + '%');
7203         }, this);
7204         
7205     }
7206     
7207 });
7208 /*
7209  * - LGPL
7210  *
7211  * Nav Progress Item
7212  * 
7213  */
7214
7215 /**
7216  * @class Roo.bootstrap.nav.ProgressBarItem
7217  * @extends Roo.bootstrap.Component
7218  * Bootstrap NavProgressBarItem class
7219  * @cfg {String} rid the reference id
7220  * @cfg {Boolean} active (true|false) Is item active default false
7221  * @cfg {Boolean} disabled (true|false) Is item active default false
7222  * @cfg {String} html
7223  * @cfg {String} position (top|bottom) text position default bottom
7224  * @cfg {String} icon show icon instead of number
7225  * 
7226  * @constructor
7227  * Create a new NavProgressBarItem
7228  * @param {Object} config The config object
7229  */
7230 Roo.bootstrap.nav.ProgressBarItem = function(config){
7231     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7232     this.addEvents({
7233         // raw events
7234         /**
7235          * @event click
7236          * The raw click event for the entire grid.
7237          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7238          * @param {Roo.EventObject} e
7239          */
7240         "click" : true
7241     });
7242    
7243 };
7244
7245 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7246     
7247     rid : '',
7248     active : false,
7249     disabled : false,
7250     html : '',
7251     position : 'bottom',
7252     icon : false,
7253     
7254     getAutoCreate : function()
7255     {
7256         var iconCls = 'roo-navigation-bar-item-icon';
7257         
7258         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7259         
7260         var cfg = {
7261             tag: 'li',
7262             cls: 'roo-navigation-bar-item',
7263             cn : [
7264                 {
7265                     tag : 'i',
7266                     cls : iconCls
7267                 }
7268             ]
7269         };
7270         
7271         if(this.active){
7272             cfg.cls += ' active';
7273         }
7274         if(this.disabled){
7275             cfg.cls += ' disabled';
7276         }
7277         
7278         return cfg;
7279     },
7280     
7281     disable : function()
7282     {
7283         this.setDisabled(true);
7284     },
7285     
7286     enable : function()
7287     {
7288         this.setDisabled(false);
7289     },
7290     
7291     initEvents: function() 
7292     {
7293         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7294         
7295         this.iconEl.on('click', this.onClick, this);
7296     },
7297     
7298     onClick : function(e)
7299     {
7300         e.preventDefault();
7301         
7302         if(this.disabled){
7303             return;
7304         }
7305         
7306         if(this.fireEvent('click', this, e) === false){
7307             return;
7308         };
7309         
7310         this.parent().setActiveItem(this);
7311     },
7312     
7313     isActive: function () 
7314     {
7315         return this.active;
7316     },
7317     
7318     setActive : function(state)
7319     {
7320         if(this.active == state){
7321             return;
7322         }
7323         
7324         this.active = state;
7325         
7326         if (state) {
7327             this.el.addClass('active');
7328             return;
7329         }
7330         
7331         this.el.removeClass('active');
7332         
7333         return;
7334     },
7335     
7336     setDisabled : function(state)
7337     {
7338         if(this.disabled == state){
7339             return;
7340         }
7341         
7342         this.disabled = state;
7343         
7344         if (state) {
7345             this.el.addClass('disabled');
7346             return;
7347         }
7348         
7349         this.el.removeClass('disabled');
7350     },
7351     
7352     tooltipEl : function()
7353     {
7354         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7355     }
7356 });
7357  
7358
7359  // depricated.
7360 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
7361 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
7362 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
7363 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
7364
7365 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
7366 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
7367
7368 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
7369 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
7370
7371 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;/*
7372  * - LGPL
7373  *
7374  *  Breadcrumb Nav
7375  * 
7376  */
7377 Roo.namespace('Roo.bootstrap.breadcrumb');
7378
7379
7380 /**
7381  * @class Roo.bootstrap.breadcrumb.Nav
7382  * @extends Roo.bootstrap.Component
7383  * Bootstrap Breadcrumb Nav Class
7384  *  
7385  * @children Roo.bootstrap.breadcrumb.Item
7386  * 
7387  * @constructor
7388  * Create a new breadcrumb.Nav
7389  * @param {Object} config The config object
7390  */
7391
7392
7393 Roo.bootstrap.breadcrumb.Nav = function(config){
7394     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7395     
7396     
7397 };
7398
7399 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7400     
7401     getAutoCreate : function()
7402     {
7403
7404         var cfg = {
7405             tag: 'nav',
7406             cn : [
7407                 {
7408                     tag : 'ol',
7409                     cls : 'breadcrumb'
7410                 }
7411             ]
7412             
7413         };
7414           
7415         return cfg;
7416     },
7417     
7418     initEvents: function()
7419     {
7420         this.olEl = this.el.select('ol',true).first();    
7421     },
7422     getChildContainer : function()
7423     {
7424         return this.olEl;  
7425     }
7426     
7427 });
7428
7429  /*
7430  * - LGPL
7431  *
7432  *  Breadcrumb Item
7433  * 
7434  */
7435
7436
7437 /**
7438  * @class Roo.bootstrap.breadcrumb.Nav
7439  * @extends Roo.bootstrap.Component
7440  * Bootstrap Breadcrumb Nav Class
7441  *  
7442  * @children Roo.bootstrap.breadcrumb.Component
7443  * @cfg {String} html the content of the link.
7444  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7445  * @cfg {Boolean} active is it active
7446
7447  * 
7448  * @constructor
7449  * Create a new breadcrumb.Nav
7450  * @param {Object} config The config object
7451  */
7452
7453 Roo.bootstrap.breadcrumb.Item = function(config){
7454     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7455     this.addEvents({
7456         // img events
7457         /**
7458          * @event click
7459          * The img click event for the img.
7460          * @param {Roo.EventObject} e
7461          */
7462         "click" : true
7463     });
7464     
7465 };
7466
7467 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7468     
7469     href: false,
7470     html : '',
7471     
7472     getAutoCreate : function()
7473     {
7474
7475         var cfg = {
7476             tag: 'li',
7477             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7478         };
7479         if (this.href !== false) {
7480             cfg.cn = [{
7481                 tag : 'a',
7482                 href : this.href,
7483                 html : this.html
7484             }];
7485         } else {
7486             cfg.html = this.html;
7487         }
7488         
7489         return cfg;
7490     },
7491     
7492     initEvents: function()
7493     {
7494         if (this.href) {
7495             this.el.select('a', true).first().on('click',this.onClick, this)
7496         }
7497         
7498     },
7499     onClick : function(e)
7500     {
7501         e.preventDefault();
7502         this.fireEvent('click',this,  e);
7503     }
7504     
7505 });
7506
7507  /*
7508  * - LGPL
7509  *
7510  * row
7511  * 
7512  */
7513
7514 /**
7515  * @class Roo.bootstrap.Row
7516  * @extends Roo.bootstrap.Component
7517  * @children Roo.bootstrap.Component
7518  * Bootstrap Row class (contains columns...)
7519  * 
7520  * @constructor
7521  * Create a new Row
7522  * @param {Object} config The config object
7523  */
7524
7525 Roo.bootstrap.Row = function(config){
7526     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7527 };
7528
7529 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7530     
7531     getAutoCreate : function(){
7532        return {
7533             cls: 'row clearfix'
7534        };
7535     }
7536     
7537     
7538 });
7539
7540  
7541
7542  /*
7543  * - LGPL
7544  *
7545  * pagination
7546  * 
7547  */
7548
7549 /**
7550  * @class Roo.bootstrap.Pagination
7551  * @extends Roo.bootstrap.Component
7552  * @children Roo.bootstrap.Pagination
7553  * Bootstrap Pagination class
7554  * 
7555  * @cfg {String} size (xs|sm|md|lg|xl)
7556  * @cfg {Boolean} inverse 
7557  * 
7558  * @constructor
7559  * Create a new Pagination
7560  * @param {Object} config The config object
7561  */
7562
7563 Roo.bootstrap.Pagination = function(config){
7564     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7565 };
7566
7567 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7568     
7569     cls: false,
7570     size: false,
7571     inverse: false,
7572     
7573     getAutoCreate : function(){
7574         var cfg = {
7575             tag: 'ul',
7576                 cls: 'pagination'
7577         };
7578         if (this.inverse) {
7579             cfg.cls += ' inverse';
7580         }
7581         if (this.html) {
7582             cfg.html=this.html;
7583         }
7584         if (this.cls) {
7585             cfg.cls += " " + this.cls;
7586         }
7587         return cfg;
7588     }
7589    
7590 });
7591
7592  
7593
7594  /*
7595  * - LGPL
7596  *
7597  * Pagination item
7598  * 
7599  */
7600
7601
7602 /**
7603  * @class Roo.bootstrap.PaginationItem
7604  * @extends Roo.bootstrap.Component
7605  * Bootstrap PaginationItem class
7606  * @cfg {String} html text
7607  * @cfg {String} href the link
7608  * @cfg {Boolean} preventDefault (true | false) default true
7609  * @cfg {Boolean} active (true | false) default false
7610  * @cfg {Boolean} disabled default false
7611  * 
7612  * 
7613  * @constructor
7614  * Create a new PaginationItem
7615  * @param {Object} config The config object
7616  */
7617
7618
7619 Roo.bootstrap.PaginationItem = function(config){
7620     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7621     this.addEvents({
7622         // raw events
7623         /**
7624          * @event click
7625          * The raw click event for the entire grid.
7626          * @param {Roo.EventObject} e
7627          */
7628         "click" : true
7629     });
7630 };
7631
7632 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7633     
7634     href : false,
7635     html : false,
7636     preventDefault: true,
7637     active : false,
7638     cls : false,
7639     disabled: false,
7640     
7641     getAutoCreate : function(){
7642         var cfg= {
7643             tag: 'li',
7644             cn: [
7645                 {
7646                     tag : 'a',
7647                     href : this.href ? this.href : '#',
7648                     html : this.html ? this.html : ''
7649                 }
7650             ]
7651         };
7652         
7653         if(this.cls){
7654             cfg.cls = this.cls;
7655         }
7656         
7657         if(this.disabled){
7658             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7659         }
7660         
7661         if(this.active){
7662             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7663         }
7664         
7665         return cfg;
7666     },
7667     
7668     initEvents: function() {
7669         
7670         this.el.on('click', this.onClick, this);
7671         
7672     },
7673     onClick : function(e)
7674     {
7675         Roo.log('PaginationItem on click ');
7676         if(this.preventDefault){
7677             e.preventDefault();
7678         }
7679         
7680         if(this.disabled){
7681             return;
7682         }
7683         
7684         this.fireEvent('click', this, e);
7685     }
7686    
7687 });
7688
7689  
7690
7691  /*
7692  * - LGPL
7693  *
7694  * slider
7695  * 
7696  */
7697
7698
7699 /**
7700  * @class Roo.bootstrap.Slider
7701  * @extends Roo.bootstrap.Component
7702  * Bootstrap Slider class
7703  *    
7704  * @constructor
7705  * Create a new Slider
7706  * @param {Object} config The config object
7707  */
7708
7709 Roo.bootstrap.Slider = function(config){
7710     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7711 };
7712
7713 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7714     
7715     getAutoCreate : function(){
7716         
7717         var cfg = {
7718             tag: 'div',
7719             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7720             cn: [
7721                 {
7722                     tag: 'a',
7723                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7724                 }
7725             ]
7726         };
7727         
7728         return cfg;
7729     }
7730    
7731 });
7732
7733  /*
7734  * Based on:
7735  * Ext JS Library 1.1.1
7736  * Copyright(c) 2006-2007, Ext JS, LLC.
7737  *
7738  * Originally Released Under LGPL - original licence link has changed is not relivant.
7739  *
7740  * Fork - LGPL
7741  * <script type="text/javascript">
7742  */
7743  /**
7744  * @extends Roo.dd.DDProxy
7745  * @class Roo.grid.SplitDragZone
7746  * Support for Column Header resizing
7747  * @constructor
7748  * @param {Object} config
7749  */
7750 // private
7751 // This is a support class used internally by the Grid components
7752 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7753     this.grid = grid;
7754     this.view = grid.getView();
7755     this.proxy = this.view.resizeProxy;
7756     Roo.grid.SplitDragZone.superclass.constructor.call(
7757         this,
7758         hd, // ID
7759         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7760         {  // CONFIG
7761             dragElId : Roo.id(this.proxy.dom),
7762             resizeFrame:false
7763         }
7764     );
7765     
7766     this.setHandleElId(Roo.id(hd));
7767     if (hd2 !== false) {
7768         this.setOuterHandleElId(Roo.id(hd2));
7769     }
7770     
7771     this.scroll = false;
7772 };
7773 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7774     fly: Roo.Element.fly,
7775
7776     b4StartDrag : function(x, y){
7777         this.view.headersDisabled = true;
7778         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7779                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7780         );
7781         this.proxy.setHeight(h);
7782         
7783         // for old system colWidth really stored the actual width?
7784         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7785         // which in reality did not work.. - it worked only for fixed sizes
7786         // for resizable we need to use actual sizes.
7787         var w = this.cm.getColumnWidth(this.cellIndex);
7788         if (!this.view.mainWrap) {
7789             // bootstrap.
7790             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7791         }
7792         
7793         
7794         
7795         // this was w-this.grid.minColumnWidth;
7796         // doesnt really make sense? - w = thie curren width or the rendered one?
7797         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7798         this.resetConstraints();
7799         this.setXConstraint(minw, 1000);
7800         this.setYConstraint(0, 0);
7801         this.minX = x - minw;
7802         this.maxX = x + 1000;
7803         this.startPos = x;
7804         if (!this.view.mainWrap) { // this is Bootstrap code..
7805             this.getDragEl().style.display='block';
7806         }
7807         
7808         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7809     },
7810
7811
7812     handleMouseDown : function(e){
7813         ev = Roo.EventObject.setEvent(e);
7814         var t = this.fly(ev.getTarget());
7815         if(t.hasClass("x-grid-split")){
7816             this.cellIndex = this.view.getCellIndex(t.dom);
7817             this.split = t.dom;
7818             this.cm = this.grid.colModel;
7819             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7820                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7821             }
7822         }
7823     },
7824
7825     endDrag : function(e){
7826         this.view.headersDisabled = false;
7827         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7828         var diff = endX - this.startPos;
7829         // 
7830         var w = this.cm.getColumnWidth(this.cellIndex);
7831         if (!this.view.mainWrap) {
7832             w = 0;
7833         }
7834         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7835     },
7836
7837     autoOffset : function(){
7838         this.setDelta(0,0);
7839     }
7840 });/*
7841  * Based on:
7842  * Ext JS Library 1.1.1
7843  * Copyright(c) 2006-2007, Ext JS, LLC.
7844  *
7845  * Originally Released Under LGPL - original licence link has changed is not relivant.
7846  *
7847  * Fork - LGPL
7848  * <script type="text/javascript">
7849  */
7850
7851 /**
7852  * @class Roo.grid.AbstractSelectionModel
7853  * @extends Roo.util.Observable
7854  * @abstract
7855  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7856  * implemented by descendant classes.  This class should not be directly instantiated.
7857  * @constructor
7858  */
7859 Roo.grid.AbstractSelectionModel = function(){
7860     this.locked = false;
7861     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7862 };
7863
7864 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7865     /** @ignore Called by the grid automatically. Do not call directly. */
7866     init : function(grid){
7867         this.grid = grid;
7868         this.initEvents();
7869     },
7870
7871     /**
7872      * Locks the selections.
7873      */
7874     lock : function(){
7875         this.locked = true;
7876     },
7877
7878     /**
7879      * Unlocks the selections.
7880      */
7881     unlock : function(){
7882         this.locked = false;
7883     },
7884
7885     /**
7886      * Returns true if the selections are locked.
7887      * @return {Boolean}
7888      */
7889     isLocked : function(){
7890         return this.locked;
7891     }
7892 });/*
7893  * Based on:
7894  * Ext JS Library 1.1.1
7895  * Copyright(c) 2006-2007, Ext JS, LLC.
7896  *
7897  * Originally Released Under LGPL - original licence link has changed is not relivant.
7898  *
7899  * Fork - LGPL
7900  * <script type="text/javascript">
7901  */
7902 /**
7903  * @extends Roo.grid.AbstractSelectionModel
7904  * @class Roo.grid.RowSelectionModel
7905  * The default SelectionModel used by {@link Roo.grid.Grid}.
7906  * It supports multiple selections and keyboard selection/navigation. 
7907  * @constructor
7908  * @param {Object} config
7909  */
7910 Roo.grid.RowSelectionModel = function(config){
7911     Roo.apply(this, config);
7912     this.selections = new Roo.util.MixedCollection(false, function(o){
7913         return o.id;
7914     });
7915
7916     this.last = false;
7917     this.lastActive = false;
7918
7919     this.addEvents({
7920         /**
7921         * @event selectionchange
7922         * Fires when the selection changes
7923         * @param {SelectionModel} this
7924         */
7925        "selectionchange" : true,
7926        /**
7927         * @event afterselectionchange
7928         * Fires after the selection changes (eg. by key press or clicking)
7929         * @param {SelectionModel} this
7930         */
7931        "afterselectionchange" : true,
7932        /**
7933         * @event beforerowselect
7934         * Fires when a row is selected being selected, return false to cancel.
7935         * @param {SelectionModel} this
7936         * @param {Number} rowIndex The selected index
7937         * @param {Boolean} keepExisting False if other selections will be cleared
7938         */
7939        "beforerowselect" : true,
7940        /**
7941         * @event rowselect
7942         * Fires when a row is selected.
7943         * @param {SelectionModel} this
7944         * @param {Number} rowIndex The selected index
7945         * @param {Roo.data.Record} r The record
7946         */
7947        "rowselect" : true,
7948        /**
7949         * @event rowdeselect
7950         * Fires when a row is deselected.
7951         * @param {SelectionModel} this
7952         * @param {Number} rowIndex The selected index
7953         */
7954         "rowdeselect" : true
7955     });
7956     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7957     this.locked = false;
7958 };
7959
7960 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7961     /**
7962      * @cfg {Boolean} singleSelect
7963      * True to allow selection of only one row at a time (defaults to false)
7964      */
7965     singleSelect : false,
7966
7967     // private
7968     initEvents : function(){
7969
7970         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7971             this.grid.on("mousedown", this.handleMouseDown, this);
7972         }else{ // allow click to work like normal
7973             this.grid.on("rowclick", this.handleDragableRowClick, this);
7974         }
7975         // bootstrap does not have a view..
7976         var view = this.grid.view ? this.grid.view : this.grid;
7977         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7978             "up" : function(e){
7979                 if(!e.shiftKey){
7980                     this.selectPrevious(e.shiftKey);
7981                 }else if(this.last !== false && this.lastActive !== false){
7982                     var last = this.last;
7983                     this.selectRange(this.last,  this.lastActive-1);
7984                     view.focusRow(this.lastActive);
7985                     if(last !== false){
7986                         this.last = last;
7987                     }
7988                 }else{
7989                     this.selectFirstRow();
7990                 }
7991                 this.fireEvent("afterselectionchange", this);
7992             },
7993             "down" : function(e){
7994                 if(!e.shiftKey){
7995                     this.selectNext(e.shiftKey);
7996                 }else if(this.last !== false && this.lastActive !== false){
7997                     var last = this.last;
7998                     this.selectRange(this.last,  this.lastActive+1);
7999                     view.focusRow(this.lastActive);
8000                     if(last !== false){
8001                         this.last = last;
8002                     }
8003                 }else{
8004                     this.selectFirstRow();
8005                 }
8006                 this.fireEvent("afterselectionchange", this);
8007             },
8008             scope: this
8009         });
8010
8011          
8012         view.on("refresh", this.onRefresh, this);
8013         view.on("rowupdated", this.onRowUpdated, this);
8014         view.on("rowremoved", this.onRemove, this);
8015     },
8016
8017     // private
8018     onRefresh : function(){
8019         var ds = this.grid.ds, i, v = this.grid.view;
8020         var s = this.selections;
8021         s.each(function(r){
8022             if((i = ds.indexOfId(r.id)) != -1){
8023                 v.onRowSelect(i);
8024                 s.add(ds.getAt(i)); // updating the selection relate data
8025             }else{
8026                 s.remove(r);
8027             }
8028         });
8029     },
8030
8031     // private
8032     onRemove : function(v, index, r){
8033         this.selections.remove(r);
8034     },
8035
8036     // private
8037     onRowUpdated : function(v, index, r){
8038         if(this.isSelected(r)){
8039             v.onRowSelect(index);
8040         }
8041     },
8042
8043     /**
8044      * Select records.
8045      * @param {Array} records The records to select
8046      * @param {Boolean} keepExisting (optional) True to keep existing selections
8047      */
8048     selectRecords : function(records, keepExisting){
8049         if(!keepExisting){
8050             this.clearSelections();
8051         }
8052         var ds = this.grid.ds;
8053         for(var i = 0, len = records.length; i < len; i++){
8054             this.selectRow(ds.indexOf(records[i]), true);
8055         }
8056     },
8057
8058     /**
8059      * Gets the number of selected rows.
8060      * @return {Number}
8061      */
8062     getCount : function(){
8063         return this.selections.length;
8064     },
8065
8066     /**
8067      * Selects the first row in the grid.
8068      */
8069     selectFirstRow : function(){
8070         this.selectRow(0);
8071     },
8072
8073     /**
8074      * Select the last row.
8075      * @param {Boolean} keepExisting (optional) True to keep existing selections
8076      */
8077     selectLastRow : function(keepExisting){
8078         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8079     },
8080
8081     /**
8082      * Selects the row immediately following the last selected row.
8083      * @param {Boolean} keepExisting (optional) True to keep existing selections
8084      */
8085     selectNext : function(keepExisting){
8086         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8087             this.selectRow(this.last+1, keepExisting);
8088             var view = this.grid.view ? this.grid.view : this.grid;
8089             view.focusRow(this.last);
8090         }
8091     },
8092
8093     /**
8094      * Selects the row that precedes the last selected row.
8095      * @param {Boolean} keepExisting (optional) True to keep existing selections
8096      */
8097     selectPrevious : function(keepExisting){
8098         if(this.last){
8099             this.selectRow(this.last-1, keepExisting);
8100             var view = this.grid.view ? this.grid.view : this.grid;
8101             view.focusRow(this.last);
8102         }
8103     },
8104
8105     /**
8106      * Returns the selected records
8107      * @return {Array} Array of selected records
8108      */
8109     getSelections : function(){
8110         return [].concat(this.selections.items);
8111     },
8112
8113     /**
8114      * Returns the first selected record.
8115      * @return {Record}
8116      */
8117     getSelected : function(){
8118         return this.selections.itemAt(0);
8119     },
8120
8121
8122     /**
8123      * Clears all selections.
8124      */
8125     clearSelections : function(fast){
8126         if(this.locked) {
8127             return;
8128         }
8129         if(fast !== true){
8130             var ds = this.grid.ds;
8131             var s = this.selections;
8132             s.each(function(r){
8133                 this.deselectRow(ds.indexOfId(r.id));
8134             }, this);
8135             s.clear();
8136         }else{
8137             this.selections.clear();
8138         }
8139         this.last = false;
8140     },
8141
8142
8143     /**
8144      * Selects all rows.
8145      */
8146     selectAll : function(){
8147         if(this.locked) {
8148             return;
8149         }
8150         this.selections.clear();
8151         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8152             this.selectRow(i, true);
8153         }
8154     },
8155
8156     /**
8157      * Returns True if there is a selection.
8158      * @return {Boolean}
8159      */
8160     hasSelection : function(){
8161         return this.selections.length > 0;
8162     },
8163
8164     /**
8165      * Returns True if the specified row is selected.
8166      * @param {Number/Record} record The record or index of the record to check
8167      * @return {Boolean}
8168      */
8169     isSelected : function(index){
8170         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8171         return (r && this.selections.key(r.id) ? true : false);
8172     },
8173
8174     /**
8175      * Returns True if the specified record id is selected.
8176      * @param {String} id The id of record to check
8177      * @return {Boolean}
8178      */
8179     isIdSelected : function(id){
8180         return (this.selections.key(id) ? true : false);
8181     },
8182
8183     // private
8184     handleMouseDown : function(e, t)
8185     {
8186         var view = this.grid.view ? this.grid.view : this.grid;
8187         var rowIndex;
8188         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8189             return;
8190         };
8191         if(e.shiftKey && this.last !== false){
8192             var last = this.last;
8193             this.selectRange(last, rowIndex, e.ctrlKey);
8194             this.last = last; // reset the last
8195             view.focusRow(rowIndex);
8196         }else{
8197             var isSelected = this.isSelected(rowIndex);
8198             if(e.button !== 0 && isSelected){
8199                 view.focusRow(rowIndex);
8200             }else if(e.ctrlKey && isSelected){
8201                 this.deselectRow(rowIndex);
8202             }else if(!isSelected){
8203                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8204                 view.focusRow(rowIndex);
8205             }
8206         }
8207         this.fireEvent("afterselectionchange", this);
8208     },
8209     // private
8210     handleDragableRowClick :  function(grid, rowIndex, e) 
8211     {
8212         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8213             this.selectRow(rowIndex, false);
8214             var view = this.grid.view ? this.grid.view : this.grid;
8215             view.focusRow(rowIndex);
8216              this.fireEvent("afterselectionchange", this);
8217         }
8218     },
8219     
8220     /**
8221      * Selects multiple rows.
8222      * @param {Array} rows Array of the indexes of the row to select
8223      * @param {Boolean} keepExisting (optional) True to keep existing selections
8224      */
8225     selectRows : function(rows, keepExisting){
8226         if(!keepExisting){
8227             this.clearSelections();
8228         }
8229         for(var i = 0, len = rows.length; i < len; i++){
8230             this.selectRow(rows[i], true);
8231         }
8232     },
8233
8234     /**
8235      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8236      * @param {Number} startRow The index of the first row in the range
8237      * @param {Number} endRow The index of the last row in the range
8238      * @param {Boolean} keepExisting (optional) True to retain existing selections
8239      */
8240     selectRange : function(startRow, endRow, keepExisting){
8241         if(this.locked) {
8242             return;
8243         }
8244         if(!keepExisting){
8245             this.clearSelections();
8246         }
8247         if(startRow <= endRow){
8248             for(var i = startRow; i <= endRow; i++){
8249                 this.selectRow(i, true);
8250             }
8251         }else{
8252             for(var i = startRow; i >= endRow; i--){
8253                 this.selectRow(i, true);
8254             }
8255         }
8256     },
8257
8258     /**
8259      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8260      * @param {Number} startRow The index of the first row in the range
8261      * @param {Number} endRow The index of the last row in the range
8262      */
8263     deselectRange : function(startRow, endRow, preventViewNotify){
8264         if(this.locked) {
8265             return;
8266         }
8267         for(var i = startRow; i <= endRow; i++){
8268             this.deselectRow(i, preventViewNotify);
8269         }
8270     },
8271
8272     /**
8273      * Selects a row.
8274      * @param {Number} row The index of the row to select
8275      * @param {Boolean} keepExisting (optional) True to keep existing selections
8276      */
8277     selectRow : function(index, keepExisting, preventViewNotify){
8278         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8279             return;
8280         }
8281         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8282             if(!keepExisting || this.singleSelect){
8283                 this.clearSelections();
8284             }
8285             var r = this.grid.ds.getAt(index);
8286             this.selections.add(r);
8287             this.last = this.lastActive = index;
8288             if(!preventViewNotify){
8289                 var view = this.grid.view ? this.grid.view : this.grid;
8290                 view.onRowSelect(index);
8291             }
8292             this.fireEvent("rowselect", this, index, r);
8293             this.fireEvent("selectionchange", this);
8294         }
8295     },
8296
8297     /**
8298      * Deselects a row.
8299      * @param {Number} row The index of the row to deselect
8300      */
8301     deselectRow : function(index, preventViewNotify){
8302         if(this.locked) {
8303             return;
8304         }
8305         if(this.last == index){
8306             this.last = false;
8307         }
8308         if(this.lastActive == index){
8309             this.lastActive = false;
8310         }
8311         var r = this.grid.ds.getAt(index);
8312         this.selections.remove(r);
8313         if(!preventViewNotify){
8314             var view = this.grid.view ? this.grid.view : this.grid;
8315             view.onRowDeselect(index);
8316         }
8317         this.fireEvent("rowdeselect", this, index);
8318         this.fireEvent("selectionchange", this);
8319     },
8320
8321     // private
8322     restoreLast : function(){
8323         if(this._last){
8324             this.last = this._last;
8325         }
8326     },
8327
8328     // private
8329     acceptsNav : function(row, col, cm){
8330         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8331     },
8332
8333     // private
8334     onEditorKey : function(field, e){
8335         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8336         if(k == e.TAB){
8337             e.stopEvent();
8338             ed.completeEdit();
8339             if(e.shiftKey){
8340                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8341             }else{
8342                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8343             }
8344         }else if(k == e.ENTER && !e.ctrlKey){
8345             e.stopEvent();
8346             ed.completeEdit();
8347             if(e.shiftKey){
8348                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8349             }else{
8350                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8351             }
8352         }else if(k == e.ESC){
8353             ed.cancelEdit();
8354         }
8355         if(newCell){
8356             g.startEditing(newCell[0], newCell[1]);
8357         }
8358     }
8359 });/*
8360  * Based on:
8361  * Ext JS Library 1.1.1
8362  * Copyright(c) 2006-2007, Ext JS, LLC.
8363  *
8364  * Originally Released Under LGPL - original licence link has changed is not relivant.
8365  *
8366  * Fork - LGPL
8367  * <script type="text/javascript">
8368  */
8369  
8370
8371 /**
8372  * @class Roo.grid.ColumnModel
8373  * @extends Roo.util.Observable
8374  * This is the default implementation of a ColumnModel used by the Grid. It defines
8375  * the columns in the grid.
8376  * <br>Usage:<br>
8377  <pre><code>
8378  var colModel = new Roo.grid.ColumnModel([
8379         {header: "Ticker", width: 60, sortable: true, locked: true},
8380         {header: "Company Name", width: 150, sortable: true},
8381         {header: "Market Cap.", width: 100, sortable: true},
8382         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8383         {header: "Employees", width: 100, sortable: true, resizable: false}
8384  ]);
8385  </code></pre>
8386  * <p>
8387  
8388  * The config options listed for this class are options which may appear in each
8389  * individual column definition.
8390  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8391  * @constructor
8392  * @param {Object} config An Array of column config objects. See this class's
8393  * config objects for details.
8394 */
8395 Roo.grid.ColumnModel = function(config){
8396         /**
8397      * The config passed into the constructor
8398      */
8399     this.config = []; //config;
8400     this.lookup = {};
8401
8402     // if no id, create one
8403     // if the column does not have a dataIndex mapping,
8404     // map it to the order it is in the config
8405     for(var i = 0, len = config.length; i < len; i++){
8406         this.addColumn(config[i]);
8407         
8408     }
8409
8410     /**
8411      * The width of columns which have no width specified (defaults to 100)
8412      * @type Number
8413      */
8414     this.defaultWidth = 100;
8415
8416     /**
8417      * Default sortable of columns which have no sortable specified (defaults to false)
8418      * @type Boolean
8419      */
8420     this.defaultSortable = false;
8421
8422     this.addEvents({
8423         /**
8424              * @event widthchange
8425              * Fires when the width of a column changes.
8426              * @param {ColumnModel} this
8427              * @param {Number} columnIndex The column index
8428              * @param {Number} newWidth The new width
8429              */
8430             "widthchange": true,
8431         /**
8432              * @event headerchange
8433              * Fires when the text of a header changes.
8434              * @param {ColumnModel} this
8435              * @param {Number} columnIndex The column index
8436              * @param {Number} newText The new header text
8437              */
8438             "headerchange": true,
8439         /**
8440              * @event hiddenchange
8441              * Fires when a column is hidden or "unhidden".
8442              * @param {ColumnModel} this
8443              * @param {Number} columnIndex The column index
8444              * @param {Boolean} hidden true if hidden, false otherwise
8445              */
8446             "hiddenchange": true,
8447             /**
8448          * @event columnmoved
8449          * Fires when a column is moved.
8450          * @param {ColumnModel} this
8451          * @param {Number} oldIndex
8452          * @param {Number} newIndex
8453          */
8454         "columnmoved" : true,
8455         /**
8456          * @event columlockchange
8457          * Fires when a column's locked state is changed
8458          * @param {ColumnModel} this
8459          * @param {Number} colIndex
8460          * @param {Boolean} locked true if locked
8461          */
8462         "columnlockchange" : true
8463     });
8464     Roo.grid.ColumnModel.superclass.constructor.call(this);
8465 };
8466 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8467     /**
8468      * @cfg {String} header The header text to display in the Grid view.
8469      */
8470         /**
8471      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8472      */
8473         /**
8474      * @cfg {String} smHeader Header at Bootsrap Small width
8475      */
8476         /**
8477      * @cfg {String} mdHeader Header at Bootsrap Medium width
8478      */
8479         /**
8480      * @cfg {String} lgHeader Header at Bootsrap Large width
8481      */
8482         /**
8483      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8484      */
8485     /**
8486      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8487      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8488      * specified, the column's index is used as an index into the Record's data Array.
8489      */
8490     /**
8491      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8492      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8493      */
8494     /**
8495      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8496      * Defaults to the value of the {@link #defaultSortable} property.
8497      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8498      */
8499     /**
8500      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8501      */
8502     /**
8503      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8504      */
8505     /**
8506      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8507      */
8508     /**
8509      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8510      */
8511     /**
8512      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8513      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8514      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8515      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8516      */
8517        /**
8518      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8519      */
8520     /**
8521      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8522      */
8523     /**
8524      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8525      */
8526     /**
8527      * @cfg {String} cursor (Optional)
8528      */
8529     /**
8530      * @cfg {String} tooltip (Optional)
8531      */
8532     /**
8533      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8534      */
8535     /**
8536      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8537      */
8538     /**
8539      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8540      */
8541     /**
8542      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8543      */
8544         /**
8545      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8546      */
8547     /**
8548      * Returns the id of the column at the specified index.
8549      * @param {Number} index The column index
8550      * @return {String} the id
8551      */
8552     getColumnId : function(index){
8553         return this.config[index].id;
8554     },
8555
8556     /**
8557      * Returns the column for a specified id.
8558      * @param {String} id The column id
8559      * @return {Object} the column
8560      */
8561     getColumnById : function(id){
8562         return this.lookup[id];
8563     },
8564
8565     
8566     /**
8567      * Returns the column Object for a specified dataIndex.
8568      * @param {String} dataIndex The column dataIndex
8569      * @return {Object|Boolean} the column or false if not found
8570      */
8571     getColumnByDataIndex: function(dataIndex){
8572         var index = this.findColumnIndex(dataIndex);
8573         return index > -1 ? this.config[index] : false;
8574     },
8575     
8576     /**
8577      * Returns the index for a specified column id.
8578      * @param {String} id The column id
8579      * @return {Number} the index, or -1 if not found
8580      */
8581     getIndexById : function(id){
8582         for(var i = 0, len = this.config.length; i < len; i++){
8583             if(this.config[i].id == id){
8584                 return i;
8585             }
8586         }
8587         return -1;
8588     },
8589     
8590     /**
8591      * Returns the index for a specified column dataIndex.
8592      * @param {String} dataIndex The column dataIndex
8593      * @return {Number} the index, or -1 if not found
8594      */
8595     
8596     findColumnIndex : function(dataIndex){
8597         for(var i = 0, len = this.config.length; i < len; i++){
8598             if(this.config[i].dataIndex == dataIndex){
8599                 return i;
8600             }
8601         }
8602         return -1;
8603     },
8604     
8605     
8606     moveColumn : function(oldIndex, newIndex){
8607         var c = this.config[oldIndex];
8608         this.config.splice(oldIndex, 1);
8609         this.config.splice(newIndex, 0, c);
8610         this.dataMap = null;
8611         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8612     },
8613
8614     isLocked : function(colIndex){
8615         return this.config[colIndex].locked === true;
8616     },
8617
8618     setLocked : function(colIndex, value, suppressEvent){
8619         if(this.isLocked(colIndex) == value){
8620             return;
8621         }
8622         this.config[colIndex].locked = value;
8623         if(!suppressEvent){
8624             this.fireEvent("columnlockchange", this, colIndex, value);
8625         }
8626     },
8627
8628     getTotalLockedWidth : function(){
8629         var totalWidth = 0;
8630         for(var i = 0; i < this.config.length; i++){
8631             if(this.isLocked(i) && !this.isHidden(i)){
8632                 this.totalWidth += this.getColumnWidth(i);
8633             }
8634         }
8635         return totalWidth;
8636     },
8637
8638     getLockedCount : function(){
8639         for(var i = 0, len = this.config.length; i < len; i++){
8640             if(!this.isLocked(i)){
8641                 return i;
8642             }
8643         }
8644         
8645         return this.config.length;
8646     },
8647
8648     /**
8649      * Returns the number of columns.
8650      * @return {Number}
8651      */
8652     getColumnCount : function(visibleOnly){
8653         if(visibleOnly === true){
8654             var c = 0;
8655             for(var i = 0, len = this.config.length; i < len; i++){
8656                 if(!this.isHidden(i)){
8657                     c++;
8658                 }
8659             }
8660             return c;
8661         }
8662         return this.config.length;
8663     },
8664
8665     /**
8666      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8667      * @param {Function} fn
8668      * @param {Object} scope (optional)
8669      * @return {Array} result
8670      */
8671     getColumnsBy : function(fn, scope){
8672         var r = [];
8673         for(var i = 0, len = this.config.length; i < len; i++){
8674             var c = this.config[i];
8675             if(fn.call(scope||this, c, i) === true){
8676                 r[r.length] = c;
8677             }
8678         }
8679         return r;
8680     },
8681
8682     /**
8683      * Returns true if the specified column is sortable.
8684      * @param {Number} col The column index
8685      * @return {Boolean}
8686      */
8687     isSortable : function(col){
8688         if(typeof this.config[col].sortable == "undefined"){
8689             return this.defaultSortable;
8690         }
8691         return this.config[col].sortable;
8692     },
8693
8694     /**
8695      * Returns the rendering (formatting) function defined for the column.
8696      * @param {Number} col The column index.
8697      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8698      */
8699     getRenderer : function(col){
8700         if(!this.config[col].renderer){
8701             return Roo.grid.ColumnModel.defaultRenderer;
8702         }
8703         return this.config[col].renderer;
8704     },
8705
8706     /**
8707      * Sets the rendering (formatting) function for a column.
8708      * @param {Number} col The column index
8709      * @param {Function} fn The function to use to process the cell's raw data
8710      * to return HTML markup for the grid view. The render function is called with
8711      * the following parameters:<ul>
8712      * <li>Data value.</li>
8713      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8714      * <li>css A CSS style string to apply to the table cell.</li>
8715      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8716      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8717      * <li>Row index</li>
8718      * <li>Column index</li>
8719      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8720      */
8721     setRenderer : function(col, fn){
8722         this.config[col].renderer = fn;
8723     },
8724
8725     /**
8726      * Returns the width for the specified column.
8727      * @param {Number} col The column index
8728      * @param (optional) {String} gridSize bootstrap width size.
8729      * @return {Number}
8730      */
8731     getColumnWidth : function(col, gridSize)
8732         {
8733                 var cfg = this.config[col];
8734                 
8735                 if (typeof(gridSize) == 'undefined') {
8736                         return cfg.width * 1 || this.defaultWidth;
8737                 }
8738                 if (gridSize === false) { // if we set it..
8739                         return cfg.width || false;
8740                 }
8741                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8742                 
8743                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8744                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8745                                 continue;
8746                         }
8747                         return cfg[ sizes[i] ];
8748                 }
8749                 return 1;
8750                 
8751     },
8752
8753     /**
8754      * Sets the width for a column.
8755      * @param {Number} col The column index
8756      * @param {Number} width The new width
8757      */
8758     setColumnWidth : function(col, width, suppressEvent){
8759         this.config[col].width = width;
8760         this.totalWidth = null;
8761         if(!suppressEvent){
8762              this.fireEvent("widthchange", this, col, width);
8763         }
8764     },
8765
8766     /**
8767      * Returns the total width of all columns.
8768      * @param {Boolean} includeHidden True to include hidden column widths
8769      * @return {Number}
8770      */
8771     getTotalWidth : function(includeHidden){
8772         if(!this.totalWidth){
8773             this.totalWidth = 0;
8774             for(var i = 0, len = this.config.length; i < len; i++){
8775                 if(includeHidden || !this.isHidden(i)){
8776                     this.totalWidth += this.getColumnWidth(i);
8777                 }
8778             }
8779         }
8780         return this.totalWidth;
8781     },
8782
8783     /**
8784      * Returns the header for the specified column.
8785      * @param {Number} col The column index
8786      * @return {String}
8787      */
8788     getColumnHeader : function(col){
8789         return this.config[col].header;
8790     },
8791
8792     /**
8793      * Sets the header for a column.
8794      * @param {Number} col The column index
8795      * @param {String} header The new header
8796      */
8797     setColumnHeader : function(col, header){
8798         this.config[col].header = header;
8799         this.fireEvent("headerchange", this, col, header);
8800     },
8801
8802     /**
8803      * Returns the tooltip for the specified column.
8804      * @param {Number} col The column index
8805      * @return {String}
8806      */
8807     getColumnTooltip : function(col){
8808             return this.config[col].tooltip;
8809     },
8810     /**
8811      * Sets the tooltip for a column.
8812      * @param {Number} col The column index
8813      * @param {String} tooltip The new tooltip
8814      */
8815     setColumnTooltip : function(col, tooltip){
8816             this.config[col].tooltip = tooltip;
8817     },
8818
8819     /**
8820      * Returns the dataIndex for the specified column.
8821      * @param {Number} col The column index
8822      * @return {Number}
8823      */
8824     getDataIndex : function(col){
8825         return this.config[col].dataIndex;
8826     },
8827
8828     /**
8829      * Sets the dataIndex for a column.
8830      * @param {Number} col The column index
8831      * @param {Number} dataIndex The new dataIndex
8832      */
8833     setDataIndex : function(col, dataIndex){
8834         this.config[col].dataIndex = dataIndex;
8835     },
8836
8837     
8838     
8839     /**
8840      * Returns true if the cell is editable.
8841      * @param {Number} colIndex The column index
8842      * @param {Number} rowIndex The row index - this is nto actually used..?
8843      * @return {Boolean}
8844      */
8845     isCellEditable : function(colIndex, rowIndex){
8846         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8847     },
8848
8849     /**
8850      * Returns the editor defined for the cell/column.
8851      * return false or null to disable editing.
8852      * @param {Number} colIndex The column index
8853      * @param {Number} rowIndex The row index
8854      * @return {Object}
8855      */
8856     getCellEditor : function(colIndex, rowIndex){
8857         return this.config[colIndex].editor;
8858     },
8859
8860     /**
8861      * Sets if a column is editable.
8862      * @param {Number} col The column index
8863      * @param {Boolean} editable True if the column is editable
8864      */
8865     setEditable : function(col, editable){
8866         this.config[col].editable = editable;
8867     },
8868
8869
8870     /**
8871      * Returns true if the column is hidden.
8872      * @param {Number} colIndex The column index
8873      * @return {Boolean}
8874      */
8875     isHidden : function(colIndex){
8876         return this.config[colIndex].hidden;
8877     },
8878
8879
8880     /**
8881      * Returns true if the column width cannot be changed
8882      */
8883     isFixed : function(colIndex){
8884         return this.config[colIndex].fixed;
8885     },
8886
8887     /**
8888      * Returns true if the column can be resized
8889      * @return {Boolean}
8890      */
8891     isResizable : function(colIndex){
8892         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8893     },
8894     /**
8895      * Sets if a column is hidden.
8896      * @param {Number} colIndex The column index
8897      * @param {Boolean} hidden True if the column is hidden
8898      */
8899     setHidden : function(colIndex, hidden){
8900         this.config[colIndex].hidden = hidden;
8901         this.totalWidth = null;
8902         this.fireEvent("hiddenchange", this, colIndex, hidden);
8903     },
8904
8905     /**
8906      * Sets the editor for a column.
8907      * @param {Number} col The column index
8908      * @param {Object} editor The editor object
8909      */
8910     setEditor : function(col, editor){
8911         this.config[col].editor = editor;
8912     },
8913     /**
8914      * Add a column (experimental...) - defaults to adding to the end..
8915      * @param {Object} config 
8916     */
8917     addColumn : function(c)
8918     {
8919     
8920         var i = this.config.length;
8921         this.config[i] = c;
8922         
8923         if(typeof c.dataIndex == "undefined"){
8924             c.dataIndex = i;
8925         }
8926         if(typeof c.renderer == "string"){
8927             c.renderer = Roo.util.Format[c.renderer];
8928         }
8929         if(typeof c.id == "undefined"){
8930             c.id = Roo.id();
8931         }
8932         if(c.editor && c.editor.xtype){
8933             c.editor  = Roo.factory(c.editor, Roo.grid);
8934         }
8935         if(c.editor && c.editor.isFormField){
8936             c.editor = new Roo.grid.GridEditor(c.editor);
8937         }
8938         this.lookup[c.id] = c;
8939     }
8940     
8941 });
8942
8943 Roo.grid.ColumnModel.defaultRenderer = function(value)
8944 {
8945     if(typeof value == "object") {
8946         return value;
8947     }
8948         if(typeof value == "string" && value.length < 1){
8949             return "&#160;";
8950         }
8951     
8952         return String.format("{0}", value);
8953 };
8954
8955 // Alias for backwards compatibility
8956 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8957 /*
8958  * Based on:
8959  * Ext JS Library 1.1.1
8960  * Copyright(c) 2006-2007, Ext JS, LLC.
8961  *
8962  * Originally Released Under LGPL - original licence link has changed is not relivant.
8963  *
8964  * Fork - LGPL
8965  * <script type="text/javascript">
8966  */
8967  
8968 /**
8969  * @class Roo.LoadMask
8970  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8971  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8972  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8973  * element's UpdateManager load indicator and will be destroyed after the initial load.
8974  * @constructor
8975  * Create a new LoadMask
8976  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8977  * @param {Object} config The config object
8978  */
8979 Roo.LoadMask = function(el, config){
8980     this.el = Roo.get(el);
8981     Roo.apply(this, config);
8982     if(this.store){
8983         this.store.on('beforeload', this.onBeforeLoad, this);
8984         this.store.on('load', this.onLoad, this);
8985         this.store.on('loadexception', this.onLoadException, this);
8986         this.removeMask = false;
8987     }else{
8988         var um = this.el.getUpdateManager();
8989         um.showLoadIndicator = false; // disable the default indicator
8990         um.on('beforeupdate', this.onBeforeLoad, this);
8991         um.on('update', this.onLoad, this);
8992         um.on('failure', this.onLoad, this);
8993         this.removeMask = true;
8994     }
8995 };
8996
8997 Roo.LoadMask.prototype = {
8998     /**
8999      * @cfg {Boolean} removeMask
9000      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9001      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9002      */
9003     removeMask : false,
9004     /**
9005      * @cfg {String} msg
9006      * The text to display in a centered loading message box (defaults to 'Loading...')
9007      */
9008     msg : 'Loading...',
9009     /**
9010      * @cfg {String} msgCls
9011      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9012      */
9013     msgCls : 'x-mask-loading',
9014
9015     /**
9016      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9017      * @type Boolean
9018      */
9019     disabled: false,
9020
9021     /**
9022      * Disables the mask to prevent it from being displayed
9023      */
9024     disable : function(){
9025        this.disabled = true;
9026     },
9027
9028     /**
9029      * Enables the mask so that it can be displayed
9030      */
9031     enable : function(){
9032         this.disabled = false;
9033     },
9034     
9035     onLoadException : function()
9036     {
9037         Roo.log(arguments);
9038         
9039         if (typeof(arguments[3]) != 'undefined') {
9040             Roo.MessageBox.alert("Error loading",arguments[3]);
9041         } 
9042         /*
9043         try {
9044             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9045                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9046             }   
9047         } catch(e) {
9048             
9049         }
9050         */
9051     
9052         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9053     },
9054     // private
9055     onLoad : function()
9056     {
9057         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9058     },
9059
9060     // private
9061     onBeforeLoad : function(){
9062         if(!this.disabled){
9063             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9064         }
9065     },
9066
9067     // private
9068     destroy : function(){
9069         if(this.store){
9070             this.store.un('beforeload', this.onBeforeLoad, this);
9071             this.store.un('load', this.onLoad, this);
9072             this.store.un('loadexception', this.onLoadException, this);
9073         }else{
9074             var um = this.el.getUpdateManager();
9075             um.un('beforeupdate', this.onBeforeLoad, this);
9076             um.un('update', this.onLoad, this);
9077             um.un('failure', this.onLoad, this);
9078         }
9079     }
9080 };/**
9081  * @class Roo.bootstrap.Table
9082  * @licence LGBL
9083  * @extends Roo.bootstrap.Component
9084  * @children Roo.bootstrap.TableBody
9085  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9086  * Similar to Roo.grid.Grid
9087  * <pre><code>
9088  var table = Roo.factory({
9089     xtype : 'Table',
9090     xns : Roo.bootstrap,
9091     autoSizeColumns: true,
9092     
9093     
9094     store : {
9095         xtype : 'Store',
9096         xns : Roo.data,
9097         remoteSort : true,
9098         sortInfo : { direction : 'ASC', field: 'name' },
9099         proxy : {
9100            xtype : 'HttpProxy',
9101            xns : Roo.data,
9102            method : 'GET',
9103            url : 'https://example.com/some.data.url.json'
9104         },
9105         reader : {
9106            xtype : 'JsonReader',
9107            xns : Roo.data,
9108            fields : [ 'id', 'name', whatever' ],
9109            id : 'id',
9110            root : 'data'
9111         }
9112     },
9113     cm : [
9114         {
9115             xtype : 'ColumnModel',
9116             xns : Roo.grid,
9117             align : 'center',
9118             cursor : 'pointer',
9119             dataIndex : 'is_in_group',
9120             header : "Name",
9121             sortable : true,
9122             renderer : function(v, x , r) {  
9123             
9124                 return String.format("{0}", v)
9125             }
9126             width : 3
9127         } // more columns..
9128     ],
9129     selModel : {
9130         xtype : 'RowSelectionModel',
9131         xns : Roo.bootstrap.Table
9132         // you can add listeners to catch selection change here....
9133     }
9134      
9135
9136  });
9137  // set any options
9138  grid.render(Roo.get("some-div"));
9139 </code></pre>
9140
9141 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9142
9143
9144
9145  *
9146  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9147  * @cfg {Roo.data.Store} store The data store to use
9148  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9149  * 
9150  * @cfg {String} cls table class
9151  *
9152  * 
9153  * @cfg {boolean} striped Should the rows be alternative striped
9154  * @cfg {boolean} bordered Add borders to the table
9155  * @cfg {boolean} hover Add hover highlighting
9156  * @cfg {boolean} condensed Format condensed
9157  * @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,
9158  *                also adds table-responsive (see bootstrap docs for details)
9159  * @cfg {Boolean} loadMask (true|false) default false
9160  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9161  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162  * @cfg {Boolean} rowSelection (true|false) default false
9163  * @cfg {Boolean} cellSelection (true|false) default false
9164  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9165  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9166  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9167  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9168  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9169  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9170  * 
9171  * @constructor
9172  * Create a new Table
9173  * @param {Object} config The config object
9174  */
9175
9176 Roo.bootstrap.Table = function(config)
9177 {
9178     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9179      
9180     // BC...
9181     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9182     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9183     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9184     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9185     
9186     this.view = this; // compat with grid.
9187     
9188     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9189     if (this.sm) {
9190         this.sm.grid = this;
9191         this.selModel = Roo.factory(this.sm, Roo.grid);
9192         this.sm = this.selModel;
9193         this.sm.xmodule = this.xmodule || false;
9194     }
9195     
9196     if (this.cm && typeof(this.cm.config) == 'undefined') {
9197         this.colModel = new Roo.grid.ColumnModel(this.cm);
9198         this.cm = this.colModel;
9199         this.cm.xmodule = this.xmodule || false;
9200     }
9201     if (this.store) {
9202         this.store= Roo.factory(this.store, Roo.data);
9203         this.ds = this.store;
9204         this.ds.xmodule = this.xmodule || false;
9205          
9206     }
9207     if (this.footer && this.store) {
9208         this.footer.dataSource = this.ds;
9209         this.footer = Roo.factory(this.footer);
9210     }
9211     
9212     /** @private */
9213     this.addEvents({
9214         /**
9215          * @event cellclick
9216          * Fires when a cell is clicked
9217          * @param {Roo.bootstrap.Table} this
9218          * @param {Roo.Element} el
9219          * @param {Number} rowIndex
9220          * @param {Number} columnIndex
9221          * @param {Roo.EventObject} e
9222          */
9223         "cellclick" : true,
9224         /**
9225          * @event celldblclick
9226          * Fires when a cell is double clicked
9227          * @param {Roo.bootstrap.Table} this
9228          * @param {Roo.Element} el
9229          * @param {Number} rowIndex
9230          * @param {Number} columnIndex
9231          * @param {Roo.EventObject} e
9232          */
9233         "celldblclick" : true,
9234         /**
9235          * @event rowclick
9236          * Fires when a row is clicked
9237          * @param {Roo.bootstrap.Table} this
9238          * @param {Roo.Element} el
9239          * @param {Number} rowIndex
9240          * @param {Roo.EventObject} e
9241          */
9242         "rowclick" : true,
9243         /**
9244          * @event rowdblclick
9245          * Fires when a row is double clicked
9246          * @param {Roo.bootstrap.Table} this
9247          * @param {Roo.Element} el
9248          * @param {Number} rowIndex
9249          * @param {Roo.EventObject} e
9250          */
9251         "rowdblclick" : true,
9252         /**
9253          * @event mouseover
9254          * Fires when a mouseover occur
9255          * @param {Roo.bootstrap.Table} this
9256          * @param {Roo.Element} el
9257          * @param {Number} rowIndex
9258          * @param {Number} columnIndex
9259          * @param {Roo.EventObject} e
9260          */
9261         "mouseover" : true,
9262         /**
9263          * @event mouseout
9264          * Fires when a mouseout occur
9265          * @param {Roo.bootstrap.Table} this
9266          * @param {Roo.Element} el
9267          * @param {Number} rowIndex
9268          * @param {Number} columnIndex
9269          * @param {Roo.EventObject} e
9270          */
9271         "mouseout" : true,
9272         /**
9273          * @event rowclass
9274          * Fires when a row is rendered, so you can change add a style to it.
9275          * @param {Roo.bootstrap.Table} this
9276          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9277          */
9278         'rowclass' : true,
9279           /**
9280          * @event rowsrendered
9281          * Fires when all the  rows have been rendered
9282          * @param {Roo.bootstrap.Table} this
9283          */
9284         'rowsrendered' : true,
9285         /**
9286          * @event contextmenu
9287          * The raw contextmenu event for the entire grid.
9288          * @param {Roo.EventObject} e
9289          */
9290         "contextmenu" : true,
9291         /**
9292          * @event rowcontextmenu
9293          * Fires when a row is right clicked
9294          * @param {Roo.bootstrap.Table} this
9295          * @param {Number} rowIndex
9296          * @param {Roo.EventObject} e
9297          */
9298         "rowcontextmenu" : true,
9299         /**
9300          * @event cellcontextmenu
9301          * Fires when a cell is right clicked
9302          * @param {Roo.bootstrap.Table} this
9303          * @param {Number} rowIndex
9304          * @param {Number} cellIndex
9305          * @param {Roo.EventObject} e
9306          */
9307          "cellcontextmenu" : true,
9308          /**
9309          * @event headercontextmenu
9310          * Fires when a header is right clicked
9311          * @param {Roo.bootstrap.Table} this
9312          * @param {Number} columnIndex
9313          * @param {Roo.EventObject} e
9314          */
9315         "headercontextmenu" : true,
9316         /**
9317          * @event mousedown
9318          * The raw mousedown event for the entire grid.
9319          * @param {Roo.EventObject} e
9320          */
9321         "mousedown" : true
9322         
9323     });
9324 };
9325
9326 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9327     
9328     cls: false,
9329     
9330     striped : false,
9331     scrollBody : false,
9332     bordered: false,
9333     hover:  false,
9334     condensed : false,
9335     responsive : false,
9336     sm : false,
9337     cm : false,
9338     store : false,
9339     loadMask : false,
9340     footerShow : true,
9341     headerShow : true,
9342     enableColumnResize: true,
9343   
9344     rowSelection : false,
9345     cellSelection : false,
9346     layout : false,
9347
9348     minColumnWidth : 50,
9349     
9350     // Roo.Element - the tbody
9351     bodyEl: false,  // <tbody> Roo.Element - thead element    
9352     headEl: false,  // <thead> Roo.Element - thead element
9353     resizeProxy : false, // proxy element for dragging?
9354
9355
9356     
9357     container: false, // used by gridpanel...
9358     
9359     lazyLoad : false,
9360     
9361     CSS : Roo.util.CSS,
9362     
9363     auto_hide_footer : false,
9364     
9365     view: false, // actually points to this..
9366     
9367     getAutoCreate : function()
9368     {
9369         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9370         
9371         cfg = {
9372             tag: 'table',
9373             cls : 'table', 
9374             cn : []
9375         };
9376         // this get's auto added by panel.Grid
9377         if (this.scrollBody) {
9378             cfg.cls += ' table-body-fixed';
9379         }    
9380         if (this.striped) {
9381             cfg.cls += ' table-striped';
9382         }
9383         
9384         if (this.hover) {
9385             cfg.cls += ' table-hover';
9386         }
9387         if (this.bordered) {
9388             cfg.cls += ' table-bordered';
9389         }
9390         if (this.condensed) {
9391             cfg.cls += ' table-condensed';
9392         }
9393         
9394         if (this.responsive) {
9395             cfg.cls += ' table-responsive';
9396         }
9397         
9398         if (this.cls) {
9399             cfg.cls+=  ' ' +this.cls;
9400         }
9401         
9402         
9403         
9404         if (this.layout) {
9405             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9406         }
9407         
9408         if(this.store || this.cm){
9409             if(this.headerShow){
9410                 cfg.cn.push(this.renderHeader());
9411             }
9412             
9413             cfg.cn.push(this.renderBody());
9414             
9415             if(this.footerShow){
9416                 cfg.cn.push(this.renderFooter());
9417             }
9418             // where does this come from?
9419             //cfg.cls+=  ' TableGrid';
9420         }
9421         
9422         return { cn : [ cfg ] };
9423     },
9424     
9425     initEvents : function()
9426     {   
9427         if(!this.store || !this.cm){
9428             return;
9429         }
9430         if (this.selModel) {
9431             this.selModel.initEvents();
9432         }
9433         
9434         
9435         //Roo.log('initEvents with ds!!!!');
9436         
9437         this.bodyEl = this.el.select('tbody', true).first();
9438         this.headEl = this.el.select('thead', true).first();
9439         this.mainFoot = this.el.select('tfoot', true).first();
9440         
9441         
9442         
9443         
9444         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9445             e.on('click', this.sort, this);
9446         }, this);
9447         
9448         
9449         // why is this done????? = it breaks dialogs??
9450         //this.parent().el.setStyle('position', 'relative');
9451         
9452         
9453         if (this.footer) {
9454             this.footer.parentId = this.id;
9455             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9456             
9457             if(this.lazyLoad){
9458                 this.el.select('tfoot tr td').first().addClass('hide');
9459             }
9460         } 
9461         
9462         if(this.loadMask) {
9463             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9464         }
9465         
9466         this.store.on('load', this.onLoad, this);
9467         this.store.on('beforeload', this.onBeforeLoad, this);
9468         this.store.on('update', this.onUpdate, this);
9469         this.store.on('add', this.onAdd, this);
9470         this.store.on("clear", this.clear, this);
9471         
9472         this.el.on("contextmenu", this.onContextMenu, this);
9473         
9474         
9475         this.cm.on("headerchange", this.onHeaderChange, this);
9476         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9477
9478  //?? does bodyEl get replaced on render?
9479         this.bodyEl.on("click", this.onClick, this);
9480         this.bodyEl.on("dblclick", this.onDblClick, this);        
9481         this.bodyEl.on('scroll', this.onBodyScroll, this);
9482
9483         // guessing mainbody will work - this relays usually caught by selmodel at present.
9484         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9485   
9486   
9487         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9488         
9489   
9490         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9491             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9492         }
9493         
9494         this.initCSS();
9495     },
9496     // Compatibility with grid - we implement all the view features at present.
9497     getView : function()
9498     {
9499         return this;
9500     },
9501     
9502     initCSS : function()
9503     {
9504         
9505         
9506         var cm = this.cm, styles = [];
9507         this.CSS.removeStyleSheet(this.id + '-cssrules');
9508         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9509         // we can honour xs/sm/md/xl  as widths...
9510         // we first have to decide what widht we are currently at...
9511         var sz = Roo.getGridSize();
9512         
9513         var total = 0;
9514         var last = -1;
9515         var cols = []; // visable cols.
9516         var total_abs = 0;
9517         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9518             var w = cm.getColumnWidth(i, false);
9519             if(cm.isHidden(i)){
9520                 cols.push( { rel : false, abs : 0 });
9521                 continue;
9522             }
9523             if (w !== false) {
9524                 cols.push( { rel : false, abs : w });
9525                 total_abs += w;
9526                 last = i; // not really..
9527                 continue;
9528             }
9529             var w = cm.getColumnWidth(i, sz);
9530             if (w > 0) {
9531                 last = i
9532             }
9533             total += w;
9534             cols.push( { rel : w, abs : false });
9535         }
9536         
9537         var avail = this.bodyEl.dom.clientWidth - total_abs;
9538         
9539         var unitWidth = Math.floor(avail / total);
9540         var rem = avail - (unitWidth * total);
9541         
9542         var hidden, width, pos = 0 , splithide , left;
9543         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9544             
9545             hidden = 'display:none;';
9546             left = '';
9547             width  = 'width:0px;';
9548             splithide = '';
9549             if(!cm.isHidden(i)){
9550                 hidden = '';
9551                 
9552                 
9553                 // we can honour xs/sm/md/xl ?
9554                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9555                 if (w===0) {
9556                     hidden = 'display:none;';
9557                 }
9558                 // width should return a small number...
9559                 if (i == last) {
9560                     w+=rem; // add the remaining with..
9561                 }
9562                 pos += w;
9563                 left = "left:" + (pos -4) + "px;";
9564                 width = "width:" + w+ "px;";
9565                 
9566             }
9567             if (this.responsive) {
9568                 width = '';
9569                 left = '';
9570                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9571                 splithide = 'display: none;';
9572             }
9573             
9574             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9575             if (this.headEl) {
9576                 if (i == last) {
9577                     splithide = 'display:none;';
9578                 }
9579                 
9580                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9581                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9582                 );
9583             }
9584             
9585         }
9586         //Roo.log(styles.join(''));
9587         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9588         
9589     },
9590     
9591     
9592     
9593     onContextMenu : function(e, t)
9594     {
9595         this.processEvent("contextmenu", e);
9596     },
9597     
9598     processEvent : function(name, e)
9599     {
9600         if (name != 'touchstart' ) {
9601             this.fireEvent(name, e);    
9602         }
9603         
9604         var t = e.getTarget();
9605         
9606         var cell = Roo.get(t);
9607         
9608         if(!cell){
9609             return;
9610         }
9611         
9612         if(cell.findParent('tfoot', false, true)){
9613             return;
9614         }
9615         
9616         if(cell.findParent('thead', false, true)){
9617             
9618             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9619                 cell = Roo.get(t).findParent('th', false, true);
9620                 if (!cell) {
9621                     Roo.log("failed to find th in thead?");
9622                     Roo.log(e.getTarget());
9623                     return;
9624                 }
9625             }
9626             
9627             var cellIndex = cell.dom.cellIndex;
9628             
9629             var ename = name == 'touchstart' ? 'click' : name;
9630             this.fireEvent("header" + ename, this, cellIndex, e);
9631             
9632             return;
9633         }
9634         
9635         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9636             cell = Roo.get(t).findParent('td', false, true);
9637             if (!cell) {
9638                 Roo.log("failed to find th in tbody?");
9639                 Roo.log(e.getTarget());
9640                 return;
9641             }
9642         }
9643         
9644         var row = cell.findParent('tr', false, true);
9645         var cellIndex = cell.dom.cellIndex;
9646         var rowIndex = row.dom.rowIndex - 1;
9647         
9648         if(row !== false){
9649             
9650             this.fireEvent("row" + name, this, rowIndex, e);
9651             
9652             if(cell !== false){
9653             
9654                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9655             }
9656         }
9657         
9658     },
9659     
9660     onMouseover : function(e, el)
9661     {
9662         var cell = Roo.get(el);
9663         
9664         if(!cell){
9665             return;
9666         }
9667         
9668         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9669             cell = cell.findParent('td', false, true);
9670         }
9671         
9672         var row = cell.findParent('tr', false, true);
9673         var cellIndex = cell.dom.cellIndex;
9674         var rowIndex = row.dom.rowIndex - 1; // start from 0
9675         
9676         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9677         
9678     },
9679     
9680     onMouseout : function(e, el)
9681     {
9682         var cell = Roo.get(el);
9683         
9684         if(!cell){
9685             return;
9686         }
9687         
9688         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9689             cell = cell.findParent('td', false, true);
9690         }
9691         
9692         var row = cell.findParent('tr', false, true);
9693         var cellIndex = cell.dom.cellIndex;
9694         var rowIndex = row.dom.rowIndex - 1; // start from 0
9695         
9696         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9697         
9698     },
9699     
9700     onClick : function(e, el)
9701     {
9702         var cell = Roo.get(el);
9703         
9704         if(!cell || (!this.cellSelection && !this.rowSelection)){
9705             return;
9706         }
9707         
9708         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9709             cell = cell.findParent('td', false, true);
9710         }
9711         
9712         if(!cell || typeof(cell) == 'undefined'){
9713             return;
9714         }
9715         
9716         var row = cell.findParent('tr', false, true);
9717         
9718         if(!row || typeof(row) == 'undefined'){
9719             return;
9720         }
9721         
9722         var cellIndex = cell.dom.cellIndex;
9723         var rowIndex = this.getRowIndex(row);
9724         
9725         // why??? - should these not be based on SelectionModel?
9726         //if(this.cellSelection){
9727             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9728         //}
9729         
9730         //if(this.rowSelection){
9731             this.fireEvent('rowclick', this, row, rowIndex, e);
9732         //}
9733          
9734     },
9735         
9736     onDblClick : function(e,el)
9737     {
9738         var cell = Roo.get(el);
9739         
9740         if(!cell || (!this.cellSelection && !this.rowSelection)){
9741             return;
9742         }
9743         
9744         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9745             cell = cell.findParent('td', false, true);
9746         }
9747         
9748         if(!cell || typeof(cell) == 'undefined'){
9749             return;
9750         }
9751         
9752         var row = cell.findParent('tr', false, true);
9753         
9754         if(!row || typeof(row) == 'undefined'){
9755             return;
9756         }
9757         
9758         var cellIndex = cell.dom.cellIndex;
9759         var rowIndex = this.getRowIndex(row);
9760         
9761         if(this.cellSelection){
9762             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9763         }
9764         
9765         if(this.rowSelection){
9766             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9767         }
9768     },
9769     findRowIndex : function(el)
9770     {
9771         var cell = Roo.get(el);
9772         if(!cell) {
9773             return false;
9774         }
9775         var row = cell.findParent('tr', false, true);
9776         
9777         if(!row || typeof(row) == 'undefined'){
9778             return false;
9779         }
9780         return this.getRowIndex(row);
9781     },
9782     sort : function(e,el)
9783     {
9784         var col = Roo.get(el);
9785         
9786         if(!col.hasClass('sortable')){
9787             return;
9788         }
9789         
9790         var sort = col.attr('sort');
9791         var dir = 'ASC';
9792         
9793         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9794             dir = 'DESC';
9795         }
9796         
9797         this.store.sortInfo = {field : sort, direction : dir};
9798         
9799         if (this.footer) {
9800             Roo.log("calling footer first");
9801             this.footer.onClick('first');
9802         } else {
9803         
9804             this.store.load({ params : { start : 0 } });
9805         }
9806     },
9807     
9808     renderHeader : function()
9809     {
9810         var header = {
9811             tag: 'thead',
9812             cn : []
9813         };
9814         
9815         var cm = this.cm;
9816         this.totalWidth = 0;
9817         
9818         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9819             
9820             var config = cm.config[i];
9821             
9822             var c = {
9823                 tag: 'th',
9824                 cls : 'x-hcol-' + i,
9825                 style : '',
9826                 
9827                 html: cm.getColumnHeader(i)
9828             };
9829             
9830             var tooltip = cm.getColumnTooltip(i);
9831             if (tooltip) {
9832                 c.tooltip = tooltip;
9833             }
9834             
9835             
9836             var hh = '';
9837             
9838             if(typeof(config.sortable) != 'undefined' && config.sortable){
9839                 c.cls += ' sortable';
9840                 c.html = '<i class="fa"></i>' + c.html;
9841             }
9842             
9843             // could use BS4 hidden-..-down 
9844             
9845             if(typeof(config.lgHeader) != 'undefined'){
9846                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9847             }
9848             
9849             if(typeof(config.mdHeader) != 'undefined'){
9850                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9851             }
9852             
9853             if(typeof(config.smHeader) != 'undefined'){
9854                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9855             }
9856             
9857             if(typeof(config.xsHeader) != 'undefined'){
9858                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9859             }
9860             
9861             if(hh.length){
9862                 c.html = hh;
9863             }
9864             
9865             if(typeof(config.tooltip) != 'undefined'){
9866                 c.tooltip = config.tooltip;
9867             }
9868             
9869             if(typeof(config.colspan) != 'undefined'){
9870                 c.colspan = config.colspan;
9871             }
9872             
9873             // hidden is handled by CSS now
9874             
9875             if(typeof(config.dataIndex) != 'undefined'){
9876                 c.sort = config.dataIndex;
9877             }
9878             
9879            
9880             
9881             if(typeof(config.align) != 'undefined' && config.align.length){
9882                 c.style += ' text-align:' + config.align + ';';
9883             }
9884             
9885             /* width is done in CSS
9886              *if(typeof(config.width) != 'undefined'){
9887                 c.style += ' width:' + config.width + 'px;';
9888                 this.totalWidth += config.width;
9889             } else {
9890                 this.totalWidth += 100; // assume minimum of 100 per column?
9891             }
9892             */
9893             
9894             if(typeof(config.cls) != 'undefined'){
9895                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9896             }
9897             // this is the bit that doesnt reall work at all...
9898             
9899             if (this.responsive) {
9900                  
9901             
9902                 ['xs','sm','md','lg'].map(function(size){
9903                     
9904                     if(typeof(config[size]) == 'undefined'){
9905                         return;
9906                     }
9907                      
9908                     if (!config[size]) { // 0 = hidden
9909                         // BS 4 '0' is treated as hide that column and below.
9910                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9911                         return;
9912                     }
9913                     
9914                     c.cls += ' col-' + size + '-' + config[size] + (
9915                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9916                     );
9917                     
9918                     
9919                 });
9920             }
9921             // at the end?
9922             
9923             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9924             
9925             
9926             
9927             
9928             header.cn.push(c)
9929         }
9930         
9931         return header;
9932     },
9933     
9934     renderBody : function()
9935     {
9936         var body = {
9937             tag: 'tbody',
9938             cn : [
9939                 {
9940                     tag: 'tr',
9941                     cn : [
9942                         {
9943                             tag : 'td',
9944                             colspan :  this.cm.getColumnCount()
9945                         }
9946                     ]
9947                 }
9948             ]
9949         };
9950         
9951         return body;
9952     },
9953     
9954     renderFooter : function()
9955     {
9956         var footer = {
9957             tag: 'tfoot',
9958             cn : [
9959                 {
9960                     tag: 'tr',
9961                     cn : [
9962                         {
9963                             tag : 'td',
9964                             colspan :  this.cm.getColumnCount()
9965                         }
9966                     ]
9967                 }
9968             ]
9969         };
9970         
9971         return footer;
9972     },
9973     
9974     
9975     
9976     onLoad : function()
9977     {
9978 //        Roo.log('ds onload');
9979         this.clear();
9980         
9981         var _this = this;
9982         var cm = this.cm;
9983         var ds = this.store;
9984         
9985         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9986             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9987             if (_this.store.sortInfo) {
9988                     
9989                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9990                     e.select('i', true).addClass(['fa-arrow-up']);
9991                 }
9992                 
9993                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9994                     e.select('i', true).addClass(['fa-arrow-down']);
9995                 }
9996             }
9997         });
9998         
9999         var tbody =  this.bodyEl;
10000               
10001         if(ds.getCount() > 0){
10002             ds.data.each(function(d,rowIndex){
10003                 var row =  this.renderRow(cm, ds, rowIndex);
10004                 
10005                 tbody.createChild(row);
10006                 
10007                 var _this = this;
10008                 
10009                 if(row.cellObjects.length){
10010                     Roo.each(row.cellObjects, function(r){
10011                         _this.renderCellObject(r);
10012                     })
10013                 }
10014                 
10015             }, this);
10016         }
10017         
10018         var tfoot = this.el.select('tfoot', true).first();
10019         
10020         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10021             
10022             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10023             
10024             var total = this.ds.getTotalCount();
10025             
10026             if(this.footer.pageSize < total){
10027                 this.mainFoot.show();
10028             }
10029         }
10030         
10031         Roo.each(this.el.select('tbody td', true).elements, function(e){
10032             e.on('mouseover', _this.onMouseover, _this);
10033         });
10034         
10035         Roo.each(this.el.select('tbody td', true).elements, function(e){
10036             e.on('mouseout', _this.onMouseout, _this);
10037         });
10038         this.fireEvent('rowsrendered', this);
10039         
10040         this.autoSize();
10041         
10042         this.initCSS(); /// resize cols
10043
10044         
10045     },
10046     
10047     
10048     onUpdate : function(ds,record)
10049     {
10050         this.refreshRow(record);
10051         this.autoSize();
10052     },
10053     
10054     onRemove : function(ds, record, index, isUpdate){
10055         if(isUpdate !== true){
10056             this.fireEvent("beforerowremoved", this, index, record);
10057         }
10058         var bt = this.bodyEl.dom;
10059         
10060         var rows = this.el.select('tbody > tr', true).elements;
10061         
10062         if(typeof(rows[index]) != 'undefined'){
10063             bt.removeChild(rows[index].dom);
10064         }
10065         
10066 //        if(bt.rows[index]){
10067 //            bt.removeChild(bt.rows[index]);
10068 //        }
10069         
10070         if(isUpdate !== true){
10071             //this.stripeRows(index);
10072             //this.syncRowHeights(index, index);
10073             //this.layout();
10074             this.fireEvent("rowremoved", this, index, record);
10075         }
10076     },
10077     
10078     onAdd : function(ds, records, rowIndex)
10079     {
10080         //Roo.log('on Add called');
10081         // - note this does not handle multiple adding very well..
10082         var bt = this.bodyEl.dom;
10083         for (var i =0 ; i < records.length;i++) {
10084             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10085             //Roo.log(records[i]);
10086             //Roo.log(this.store.getAt(rowIndex+i));
10087             this.insertRow(this.store, rowIndex + i, false);
10088             return;
10089         }
10090         
10091     },
10092     
10093     
10094     refreshRow : function(record){
10095         var ds = this.store, index;
10096         if(typeof record == 'number'){
10097             index = record;
10098             record = ds.getAt(index);
10099         }else{
10100             index = ds.indexOf(record);
10101             if (index < 0) {
10102                 return; // should not happen - but seems to 
10103             }
10104         }
10105         this.insertRow(ds, index, true);
10106         this.autoSize();
10107         this.onRemove(ds, record, index+1, true);
10108         this.autoSize();
10109         //this.syncRowHeights(index, index);
10110         //this.layout();
10111         this.fireEvent("rowupdated", this, index, record);
10112     },
10113     // private - called by RowSelection
10114     onRowSelect : function(rowIndex){
10115         var row = this.getRowDom(rowIndex);
10116         row.addClass(['bg-info','info']);
10117     },
10118     // private - called by RowSelection
10119     onRowDeselect : function(rowIndex)
10120     {
10121         if (rowIndex < 0) {
10122             return;
10123         }
10124         var row = this.getRowDom(rowIndex);
10125         row.removeClass(['bg-info','info']);
10126     },
10127       /**
10128      * Focuses the specified row.
10129      * @param {Number} row The row index
10130      */
10131     focusRow : function(row)
10132     {
10133         //Roo.log('GridView.focusRow');
10134         var x = this.bodyEl.dom.scrollLeft;
10135         this.focusCell(row, 0, false);
10136         this.bodyEl.dom.scrollLeft = x;
10137
10138     },
10139      /**
10140      * Focuses the specified cell.
10141      * @param {Number} row The row index
10142      * @param {Number} col The column index
10143      * @param {Boolean} hscroll false to disable horizontal scrolling
10144      */
10145     focusCell : function(row, col, hscroll)
10146     {
10147         //Roo.log('GridView.focusCell');
10148         var el = this.ensureVisible(row, col, hscroll);
10149         // not sure what focusEL achives = it's a <a> pos relative 
10150         //this.focusEl.alignTo(el, "tl-tl");
10151         //if(Roo.isGecko){
10152         //    this.focusEl.focus();
10153         //}else{
10154         //    this.focusEl.focus.defer(1, this.focusEl);
10155         //}
10156     },
10157     
10158      /**
10159      * Scrolls the specified cell into view
10160      * @param {Number} row The row index
10161      * @param {Number} col The column index
10162      * @param {Boolean} hscroll false to disable horizontal scrolling
10163      */
10164     ensureVisible : function(row, col, hscroll)
10165     {
10166         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10167         //return null; //disable for testing.
10168         if(typeof row != "number"){
10169             row = row.rowIndex;
10170         }
10171         if(row < 0 && row >= this.ds.getCount()){
10172             return  null;
10173         }
10174         col = (col !== undefined ? col : 0);
10175         var cm = this.cm;
10176         while(cm.isHidden(col)){
10177             col++;
10178         }
10179
10180         var el = this.getCellDom(row, col);
10181         if(!el){
10182             return null;
10183         }
10184         var c = this.bodyEl.dom;
10185
10186         var ctop = parseInt(el.offsetTop, 10);
10187         var cleft = parseInt(el.offsetLeft, 10);
10188         var cbot = ctop + el.offsetHeight;
10189         var cright = cleft + el.offsetWidth;
10190
10191         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10192         var ch = 0; //?? header is not withing the area?
10193         var stop = parseInt(c.scrollTop, 10);
10194         var sleft = parseInt(c.scrollLeft, 10);
10195         var sbot = stop + ch;
10196         var sright = sleft + c.clientWidth;
10197         /*
10198         Roo.log('GridView.ensureVisible:' +
10199                 ' ctop:' + ctop +
10200                 ' c.clientHeight:' + c.clientHeight +
10201                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10202                 ' stop:' + stop +
10203                 ' cbot:' + cbot +
10204                 ' sbot:' + sbot +
10205                 ' ch:' + ch  
10206                 );
10207         */
10208         if(ctop < stop){
10209             c.scrollTop = ctop;
10210             //Roo.log("set scrolltop to ctop DISABLE?");
10211         }else if(cbot > sbot){
10212             //Roo.log("set scrolltop to cbot-ch");
10213             c.scrollTop = cbot-ch;
10214         }
10215
10216         if(hscroll !== false){
10217             if(cleft < sleft){
10218                 c.scrollLeft = cleft;
10219             }else if(cright > sright){
10220                 c.scrollLeft = cright-c.clientWidth;
10221             }
10222         }
10223
10224         return el;
10225     },
10226     
10227     
10228     insertRow : function(dm, rowIndex, isUpdate){
10229         
10230         if(!isUpdate){
10231             this.fireEvent("beforerowsinserted", this, rowIndex);
10232         }
10233             //var s = this.getScrollState();
10234         var row = this.renderRow(this.cm, this.store, rowIndex);
10235         // insert before rowIndex..
10236         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10237         
10238         var _this = this;
10239                 
10240         if(row.cellObjects.length){
10241             Roo.each(row.cellObjects, function(r){
10242                 _this.renderCellObject(r);
10243             })
10244         }
10245             
10246         if(!isUpdate){
10247             this.fireEvent("rowsinserted", this, rowIndex);
10248             //this.syncRowHeights(firstRow, lastRow);
10249             //this.stripeRows(firstRow);
10250             //this.layout();
10251         }
10252         
10253     },
10254     
10255     
10256     getRowDom : function(rowIndex)
10257     {
10258         var rows = this.el.select('tbody > tr', true).elements;
10259         
10260         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10261         
10262     },
10263     getCellDom : function(rowIndex, colIndex)
10264     {
10265         var row = this.getRowDom(rowIndex);
10266         if (row === false) {
10267             return false;
10268         }
10269         var cols = row.select('td', true).elements;
10270         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10271         
10272     },
10273     
10274     // returns the object tree for a tr..
10275   
10276     
10277     renderRow : function(cm, ds, rowIndex) 
10278     {
10279         var d = ds.getAt(rowIndex);
10280         
10281         var row = {
10282             tag : 'tr',
10283             cls : 'x-row-' + rowIndex,
10284             cn : []
10285         };
10286             
10287         var cellObjects = [];
10288         
10289         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10290             var config = cm.config[i];
10291             
10292             var renderer = cm.getRenderer(i);
10293             var value = '';
10294             var id = false;
10295             
10296             if(typeof(renderer) !== 'undefined'){
10297                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10298             }
10299             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10300             // and are rendered into the cells after the row is rendered - using the id for the element.
10301             
10302             if(typeof(value) === 'object'){
10303                 id = Roo.id();
10304                 cellObjects.push({
10305                     container : id,
10306                     cfg : value 
10307                 })
10308             }
10309             
10310             var rowcfg = {
10311                 record: d,
10312                 rowIndex : rowIndex,
10313                 colIndex : i,
10314                 rowClass : ''
10315             };
10316
10317             this.fireEvent('rowclass', this, rowcfg);
10318             
10319             var td = {
10320                 tag: 'td',
10321                 // this might end up displaying HTML?
10322                 // this is too messy... - better to only do it on columsn you know are going to be too long
10323                 //tooltip : (typeof(value) === 'object') ? '' : value,
10324                 cls : rowcfg.rowClass + ' x-col-' + i,
10325                 style: '',
10326                 html: (typeof(value) === 'object') ? '' : value
10327             };
10328             
10329             if (id) {
10330                 td.id = id;
10331             }
10332             
10333             if(typeof(config.colspan) != 'undefined'){
10334                 td.colspan = config.colspan;
10335             }
10336             
10337             
10338             
10339             if(typeof(config.align) != 'undefined' && config.align.length){
10340                 td.style += ' text-align:' + config.align + ';';
10341             }
10342             if(typeof(config.valign) != 'undefined' && config.valign.length){
10343                 td.style += ' vertical-align:' + config.valign + ';';
10344             }
10345             /*
10346             if(typeof(config.width) != 'undefined'){
10347                 td.style += ' width:' +  config.width + 'px;';
10348             }
10349             */
10350             
10351             if(typeof(config.cursor) != 'undefined'){
10352                 td.style += ' cursor:' +  config.cursor + ';';
10353             }
10354             
10355             if(typeof(config.cls) != 'undefined'){
10356                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10357             }
10358             if (this.responsive) {
10359                 ['xs','sm','md','lg'].map(function(size){
10360                     
10361                     if(typeof(config[size]) == 'undefined'){
10362                         return;
10363                     }
10364                     
10365                     
10366                       
10367                     if (!config[size]) { // 0 = hidden
10368                         // BS 4 '0' is treated as hide that column and below.
10369                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10370                         return;
10371                     }
10372                     
10373                     td.cls += ' col-' + size + '-' + config[size] + (
10374                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10375                     );
10376                      
10377     
10378                 });
10379             }
10380             row.cn.push(td);
10381            
10382         }
10383         
10384         row.cellObjects = cellObjects;
10385         
10386         return row;
10387           
10388     },
10389     
10390     
10391     
10392     onBeforeLoad : function()
10393     {
10394         
10395     },
10396      /**
10397      * Remove all rows
10398      */
10399     clear : function()
10400     {
10401         this.el.select('tbody', true).first().dom.innerHTML = '';
10402     },
10403     /**
10404      * Show or hide a row.
10405      * @param {Number} rowIndex to show or hide
10406      * @param {Boolean} state hide
10407      */
10408     setRowVisibility : function(rowIndex, state)
10409     {
10410         var bt = this.bodyEl.dom;
10411         
10412         var rows = this.el.select('tbody > tr', true).elements;
10413         
10414         if(typeof(rows[rowIndex]) == 'undefined'){
10415             return;
10416         }
10417         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10418         
10419     },
10420     
10421     
10422     getSelectionModel : function(){
10423         if(!this.selModel){
10424             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10425         }
10426         return this.selModel;
10427     },
10428     /*
10429      * Render the Roo.bootstrap object from renderder
10430      */
10431     renderCellObject : function(r)
10432     {
10433         var _this = this;
10434         
10435         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10436         
10437         var t = r.cfg.render(r.container);
10438         
10439         if(r.cfg.cn){
10440             Roo.each(r.cfg.cn, function(c){
10441                 var child = {
10442                     container: t.getChildContainer(),
10443                     cfg: c
10444                 };
10445                 _this.renderCellObject(child);
10446             })
10447         }
10448     },
10449     /**
10450      * get the Row Index from a dom element.
10451      * @param {Roo.Element} row The row to look for
10452      * @returns {Number} the row
10453      */
10454     getRowIndex : function(row)
10455     {
10456         var rowIndex = -1;
10457         
10458         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10459             if(el != row){
10460                 return;
10461             }
10462             
10463             rowIndex = index;
10464         });
10465         
10466         return rowIndex;
10467     },
10468     /**
10469      * get the header TH element for columnIndex
10470      * @param {Number} columnIndex
10471      * @returns {Roo.Element}
10472      */
10473     getHeaderIndex: function(colIndex)
10474     {
10475         var cols = this.headEl.select('th', true).elements;
10476         return cols[colIndex]; 
10477     },
10478     /**
10479      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10480      * @param {domElement} cell to look for
10481      * @returns {Number} the column
10482      */
10483     getCellIndex : function(cell)
10484     {
10485         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10486         if(id){
10487             return parseInt(id[1], 10);
10488         }
10489         return 0;
10490     },
10491      /**
10492      * Returns the grid's underlying element = used by panel.Grid
10493      * @return {Element} The element
10494      */
10495     getGridEl : function(){
10496         return this.el;
10497     },
10498      /**
10499      * Forces a resize - used by panel.Grid
10500      * @return {Element} The element
10501      */
10502     autoSize : function()
10503     {
10504         //var ctr = Roo.get(this.container.dom.parentElement);
10505         var ctr = Roo.get(this.el.dom);
10506         
10507         var thd = this.getGridEl().select('thead',true).first();
10508         var tbd = this.getGridEl().select('tbody', true).first();
10509         var tfd = this.getGridEl().select('tfoot', true).first();
10510         
10511         var cw = ctr.getWidth();
10512         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10513         
10514         if (tbd) {
10515             
10516             tbd.setWidth(ctr.getWidth());
10517             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10518             // this needs fixing for various usage - currently only hydra job advers I think..
10519             //tdb.setHeight(
10520             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10521             //); 
10522             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10523             cw -= barsize;
10524         }
10525         cw = Math.max(cw, this.totalWidth);
10526         this.getGridEl().select('tbody tr',true).setWidth(cw);
10527         this.initCSS();
10528         
10529         // resize 'expandable coloumn?
10530         
10531         return; // we doe not have a view in this design..
10532         
10533     },
10534     onBodyScroll: function()
10535     {
10536         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10537         if(this.headEl){
10538             this.headEl.setStyle({
10539                 'position' : 'relative',
10540                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10541             });
10542         }
10543         
10544         if(this.lazyLoad){
10545             
10546             var scrollHeight = this.bodyEl.dom.scrollHeight;
10547             
10548             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10549             
10550             var height = this.bodyEl.getHeight();
10551             
10552             if(scrollHeight - height == scrollTop) {
10553                 
10554                 var total = this.ds.getTotalCount();
10555                 
10556                 if(this.footer.cursor + this.footer.pageSize < total){
10557                     
10558                     this.footer.ds.load({
10559                         params : {
10560                             start : this.footer.cursor + this.footer.pageSize,
10561                             limit : this.footer.pageSize
10562                         },
10563                         add : true
10564                     });
10565                 }
10566             }
10567             
10568         }
10569     },
10570     onColumnSplitterMoved : function(i, diff)
10571     {
10572         this.userResized = true;
10573         
10574         var cm = this.colModel;
10575         
10576         var w = this.getHeaderIndex(i).getWidth() + diff;
10577         
10578         
10579         cm.setColumnWidth(i, w, true);
10580         this.initCSS();
10581         //var cid = cm.getColumnId(i); << not used in this version?
10582        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10583         
10584         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10585         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10586         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10587 */
10588         //this.updateSplitters();
10589         //this.layout(); << ??
10590         this.fireEvent("columnresize", i, w);
10591     },
10592     onHeaderChange : function()
10593     {
10594         var header = this.renderHeader();
10595         var table = this.el.select('table', true).first();
10596         
10597         this.headEl.remove();
10598         this.headEl = table.createChild(header, this.bodyEl, false);
10599         
10600         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10601             e.on('click', this.sort, this);
10602         }, this);
10603         
10604         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10605             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10606         }
10607         
10608     },
10609     
10610     onHiddenChange : function(colModel, colIndex, hidden)
10611     {
10612         /*
10613         this.cm.setHidden()
10614         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10615         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10616         
10617         this.CSS.updateRule(thSelector, "display", "");
10618         this.CSS.updateRule(tdSelector, "display", "");
10619         
10620         if(hidden){
10621             this.CSS.updateRule(thSelector, "display", "none");
10622             this.CSS.updateRule(tdSelector, "display", "none");
10623         }
10624         */
10625         // onload calls initCSS()
10626         this.onHeaderChange();
10627         this.onLoad();
10628     },
10629     
10630     setColumnWidth: function(col_index, width)
10631     {
10632         // width = "md-2 xs-2..."
10633         if(!this.colModel.config[col_index]) {
10634             return;
10635         }
10636         
10637         var w = width.split(" ");
10638         
10639         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10640         
10641         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10642         
10643         
10644         for(var j = 0; j < w.length; j++) {
10645             
10646             if(!w[j]) {
10647                 continue;
10648             }
10649             
10650             var size_cls = w[j].split("-");
10651             
10652             if(!Number.isInteger(size_cls[1] * 1)) {
10653                 continue;
10654             }
10655             
10656             if(!this.colModel.config[col_index][size_cls[0]]) {
10657                 continue;
10658             }
10659             
10660             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10661                 continue;
10662             }
10663             
10664             h_row[0].classList.replace(
10665                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10666                 "col-"+size_cls[0]+"-"+size_cls[1]
10667             );
10668             
10669             for(var i = 0; i < rows.length; i++) {
10670                 
10671                 var size_cls = w[j].split("-");
10672                 
10673                 if(!Number.isInteger(size_cls[1] * 1)) {
10674                     continue;
10675                 }
10676                 
10677                 if(!this.colModel.config[col_index][size_cls[0]]) {
10678                     continue;
10679                 }
10680                 
10681                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10682                     continue;
10683                 }
10684                 
10685                 rows[i].classList.replace(
10686                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10687                     "col-"+size_cls[0]+"-"+size_cls[1]
10688                 );
10689             }
10690             
10691             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10692         }
10693     }
10694 });
10695
10696 // currently only used to find the split on drag.. 
10697 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10698
10699 /**
10700  * @depricated
10701 */
10702 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10703 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10704 /*
10705  * - LGPL
10706  *
10707  * table cell
10708  * 
10709  */
10710
10711 /**
10712  * @class Roo.bootstrap.TableCell
10713  * @extends Roo.bootstrap.Component
10714  * @children Roo.bootstrap.Component
10715  * @parent Roo.bootstrap.TableRow
10716  * Bootstrap TableCell class
10717  * 
10718  * @cfg {String} html cell contain text
10719  * @cfg {String} cls cell class
10720  * @cfg {String} tag cell tag (td|th) default td
10721  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10722  * @cfg {String} align Aligns the content in a cell
10723  * @cfg {String} axis Categorizes cells
10724  * @cfg {String} bgcolor Specifies the background color of a cell
10725  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10726  * @cfg {Number} colspan Specifies the number of columns a cell should span
10727  * @cfg {String} headers Specifies one or more header cells a cell is related to
10728  * @cfg {Number} height Sets the height of a cell
10729  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10730  * @cfg {Number} rowspan Sets the number of rows a cell should span
10731  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10732  * @cfg {String} valign Vertical aligns the content in a cell
10733  * @cfg {Number} width Specifies the width of a cell
10734  * 
10735  * @constructor
10736  * Create a new TableCell
10737  * @param {Object} config The config object
10738  */
10739
10740 Roo.bootstrap.TableCell = function(config){
10741     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10742 };
10743
10744 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10745     
10746     html: false,
10747     cls: false,
10748     tag: false,
10749     abbr: false,
10750     align: false,
10751     axis: false,
10752     bgcolor: false,
10753     charoff: false,
10754     colspan: false,
10755     headers: false,
10756     height: false,
10757     nowrap: false,
10758     rowspan: false,
10759     scope: false,
10760     valign: false,
10761     width: false,
10762     
10763     
10764     getAutoCreate : function(){
10765         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10766         
10767         cfg = {
10768             tag: 'td'
10769         };
10770         
10771         if(this.tag){
10772             cfg.tag = this.tag;
10773         }
10774         
10775         if (this.html) {
10776             cfg.html=this.html
10777         }
10778         if (this.cls) {
10779             cfg.cls=this.cls
10780         }
10781         if (this.abbr) {
10782             cfg.abbr=this.abbr
10783         }
10784         if (this.align) {
10785             cfg.align=this.align
10786         }
10787         if (this.axis) {
10788             cfg.axis=this.axis
10789         }
10790         if (this.bgcolor) {
10791             cfg.bgcolor=this.bgcolor
10792         }
10793         if (this.charoff) {
10794             cfg.charoff=this.charoff
10795         }
10796         if (this.colspan) {
10797             cfg.colspan=this.colspan
10798         }
10799         if (this.headers) {
10800             cfg.headers=this.headers
10801         }
10802         if (this.height) {
10803             cfg.height=this.height
10804         }
10805         if (this.nowrap) {
10806             cfg.nowrap=this.nowrap
10807         }
10808         if (this.rowspan) {
10809             cfg.rowspan=this.rowspan
10810         }
10811         if (this.scope) {
10812             cfg.scope=this.scope
10813         }
10814         if (this.valign) {
10815             cfg.valign=this.valign
10816         }
10817         if (this.width) {
10818             cfg.width=this.width
10819         }
10820         
10821         
10822         return cfg;
10823     }
10824    
10825 });
10826
10827  
10828
10829  /*
10830  * - LGPL
10831  *
10832  * table row
10833  * 
10834  */
10835
10836 /**
10837  * @class Roo.bootstrap.TableRow
10838  * @extends Roo.bootstrap.Component
10839  * @children Roo.bootstrap.TableCell
10840  * @parent Roo.bootstrap.TableBody
10841  * Bootstrap TableRow class
10842  * @cfg {String} cls row class
10843  * @cfg {String} align Aligns the content in a table row
10844  * @cfg {String} bgcolor Specifies a background color for a table row
10845  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10846  * @cfg {String} valign Vertical aligns the content in a table row
10847  * 
10848  * @constructor
10849  * Create a new TableRow
10850  * @param {Object} config The config object
10851  */
10852
10853 Roo.bootstrap.TableRow = function(config){
10854     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10855 };
10856
10857 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10858     
10859     cls: false,
10860     align: false,
10861     bgcolor: false,
10862     charoff: false,
10863     valign: false,
10864     
10865     getAutoCreate : function(){
10866         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10867         
10868         cfg = {
10869             tag: 'tr'
10870         };
10871             
10872         if(this.cls){
10873             cfg.cls = this.cls;
10874         }
10875         if(this.align){
10876             cfg.align = this.align;
10877         }
10878         if(this.bgcolor){
10879             cfg.bgcolor = this.bgcolor;
10880         }
10881         if(this.charoff){
10882             cfg.charoff = this.charoff;
10883         }
10884         if(this.valign){
10885             cfg.valign = this.valign;
10886         }
10887         
10888         return cfg;
10889     }
10890    
10891 });
10892
10893  
10894
10895  /*
10896  * - LGPL
10897  *
10898  * table body
10899  * 
10900  */
10901
10902 /**
10903  * @class Roo.bootstrap.TableBody
10904  * @extends Roo.bootstrap.Component
10905  * @children Roo.bootstrap.TableRow
10906  * @parent Roo.bootstrap.Table
10907  * Bootstrap TableBody class
10908  * @cfg {String} cls element class
10909  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10910  * @cfg {String} align Aligns the content inside the element
10911  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10912  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10913  * 
10914  * @constructor
10915  * Create a new TableBody
10916  * @param {Object} config The config object
10917  */
10918
10919 Roo.bootstrap.TableBody = function(config){
10920     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10921 };
10922
10923 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10924     
10925     cls: false,
10926     tag: false,
10927     align: false,
10928     charoff: false,
10929     valign: false,
10930     
10931     getAutoCreate : function(){
10932         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10933         
10934         cfg = {
10935             tag: 'tbody'
10936         };
10937             
10938         if (this.cls) {
10939             cfg.cls=this.cls
10940         }
10941         if(this.tag){
10942             cfg.tag = this.tag;
10943         }
10944         
10945         if(this.align){
10946             cfg.align = this.align;
10947         }
10948         if(this.charoff){
10949             cfg.charoff = this.charoff;
10950         }
10951         if(this.valign){
10952             cfg.valign = this.valign;
10953         }
10954         
10955         return cfg;
10956     }
10957     
10958     
10959 //    initEvents : function()
10960 //    {
10961 //        
10962 //        if(!this.store){
10963 //            return;
10964 //        }
10965 //        
10966 //        this.store = Roo.factory(this.store, Roo.data);
10967 //        this.store.on('load', this.onLoad, this);
10968 //        
10969 //        this.store.load();
10970 //        
10971 //    },
10972 //    
10973 //    onLoad: function () 
10974 //    {   
10975 //        this.fireEvent('load', this);
10976 //    }
10977 //    
10978 //   
10979 });
10980
10981  
10982
10983  /*
10984  * Based on:
10985  * Ext JS Library 1.1.1
10986  * Copyright(c) 2006-2007, Ext JS, LLC.
10987  *
10988  * Originally Released Under LGPL - original licence link has changed is not relivant.
10989  *
10990  * Fork - LGPL
10991  * <script type="text/javascript">
10992  */
10993
10994 // as we use this in bootstrap.
10995 Roo.namespace('Roo.form');
10996  /**
10997  * @class Roo.form.Action
10998  * Internal Class used to handle form actions
10999  * @constructor
11000  * @param {Roo.form.BasicForm} el The form element or its id
11001  * @param {Object} config Configuration options
11002  */
11003
11004  
11005  
11006 // define the action interface
11007 Roo.form.Action = function(form, options){
11008     this.form = form;
11009     this.options = options || {};
11010 };
11011 /**
11012  * Client Validation Failed
11013  * @const 
11014  */
11015 Roo.form.Action.CLIENT_INVALID = 'client';
11016 /**
11017  * Server Validation Failed
11018  * @const 
11019  */
11020 Roo.form.Action.SERVER_INVALID = 'server';
11021  /**
11022  * Connect to Server Failed
11023  * @const 
11024  */
11025 Roo.form.Action.CONNECT_FAILURE = 'connect';
11026 /**
11027  * Reading Data from Server Failed
11028  * @const 
11029  */
11030 Roo.form.Action.LOAD_FAILURE = 'load';
11031
11032 Roo.form.Action.prototype = {
11033     type : 'default',
11034     failureType : undefined,
11035     response : undefined,
11036     result : undefined,
11037
11038     // interface method
11039     run : function(options){
11040
11041     },
11042
11043     // interface method
11044     success : function(response){
11045
11046     },
11047
11048     // interface method
11049     handleResponse : function(response){
11050
11051     },
11052
11053     // default connection failure
11054     failure : function(response){
11055         
11056         this.response = response;
11057         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11058         this.form.afterAction(this, false);
11059     },
11060
11061     processResponse : function(response){
11062         this.response = response;
11063         if(!response.responseText){
11064             return true;
11065         }
11066         this.result = this.handleResponse(response);
11067         return this.result;
11068     },
11069
11070     // utility functions used internally
11071     getUrl : function(appendParams){
11072         var url = this.options.url || this.form.url || this.form.el.dom.action;
11073         if(appendParams){
11074             var p = this.getParams();
11075             if(p){
11076                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11077             }
11078         }
11079         return url;
11080     },
11081
11082     getMethod : function(){
11083         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11084     },
11085
11086     getParams : function(){
11087         var bp = this.form.baseParams;
11088         var p = this.options.params;
11089         if(p){
11090             if(typeof p == "object"){
11091                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11092             }else if(typeof p == 'string' && bp){
11093                 p += '&' + Roo.urlEncode(bp);
11094             }
11095         }else if(bp){
11096             p = Roo.urlEncode(bp);
11097         }
11098         return p;
11099     },
11100
11101     createCallback : function(){
11102         return {
11103             success: this.success,
11104             failure: this.failure,
11105             scope: this,
11106             timeout: (this.form.timeout*1000),
11107             upload: this.form.fileUpload ? this.success : undefined
11108         };
11109     }
11110 };
11111
11112 Roo.form.Action.Submit = function(form, options){
11113     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11114 };
11115
11116 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11117     type : 'submit',
11118
11119     haveProgress : false,
11120     uploadComplete : false,
11121     
11122     // uploadProgress indicator.
11123     uploadProgress : function()
11124     {
11125         if (!this.form.progressUrl) {
11126             return;
11127         }
11128         
11129         if (!this.haveProgress) {
11130             Roo.MessageBox.progress("Uploading", "Uploading");
11131         }
11132         if (this.uploadComplete) {
11133            Roo.MessageBox.hide();
11134            return;
11135         }
11136         
11137         this.haveProgress = true;
11138    
11139         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11140         
11141         var c = new Roo.data.Connection();
11142         c.request({
11143             url : this.form.progressUrl,
11144             params: {
11145                 id : uid
11146             },
11147             method: 'GET',
11148             success : function(req){
11149                //console.log(data);
11150                 var rdata = false;
11151                 var edata;
11152                 try  {
11153                    rdata = Roo.decode(req.responseText)
11154                 } catch (e) {
11155                     Roo.log("Invalid data from server..");
11156                     Roo.log(edata);
11157                     return;
11158                 }
11159                 if (!rdata || !rdata.success) {
11160                     Roo.log(rdata);
11161                     Roo.MessageBox.alert(Roo.encode(rdata));
11162                     return;
11163                 }
11164                 var data = rdata.data;
11165                 
11166                 if (this.uploadComplete) {
11167                    Roo.MessageBox.hide();
11168                    return;
11169                 }
11170                    
11171                 if (data){
11172                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11173                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11174                     );
11175                 }
11176                 this.uploadProgress.defer(2000,this);
11177             },
11178        
11179             failure: function(data) {
11180                 Roo.log('progress url failed ');
11181                 Roo.log(data);
11182             },
11183             scope : this
11184         });
11185            
11186     },
11187     
11188     
11189     run : function()
11190     {
11191         // run get Values on the form, so it syncs any secondary forms.
11192         this.form.getValues();
11193         
11194         var o = this.options;
11195         var method = this.getMethod();
11196         var isPost = method == 'POST';
11197         if(o.clientValidation === false || this.form.isValid()){
11198             
11199             if (this.form.progressUrl) {
11200                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11201                     (new Date() * 1) + '' + Math.random());
11202                     
11203             } 
11204             
11205             
11206             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11207                 form:this.form.el.dom,
11208                 url:this.getUrl(!isPost),
11209                 method: method,
11210                 params:isPost ? this.getParams() : null,
11211                 isUpload: this.form.fileUpload,
11212                 formData : this.form.formData
11213             }));
11214             
11215             this.uploadProgress();
11216
11217         }else if (o.clientValidation !== false){ // client validation failed
11218             this.failureType = Roo.form.Action.CLIENT_INVALID;
11219             this.form.afterAction(this, false);
11220         }
11221     },
11222
11223     success : function(response)
11224     {
11225         this.uploadComplete= true;
11226         if (this.haveProgress) {
11227             Roo.MessageBox.hide();
11228         }
11229         
11230         
11231         var result = this.processResponse(response);
11232         if(result === true || result.success){
11233             this.form.afterAction(this, true);
11234             return;
11235         }
11236         if(result.errors){
11237             this.form.markInvalid(result.errors);
11238             this.failureType = Roo.form.Action.SERVER_INVALID;
11239         }
11240         this.form.afterAction(this, false);
11241     },
11242     failure : function(response)
11243     {
11244         this.uploadComplete= true;
11245         if (this.haveProgress) {
11246             Roo.MessageBox.hide();
11247         }
11248         
11249         this.response = response;
11250         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11251         this.form.afterAction(this, false);
11252     },
11253     
11254     handleResponse : function(response){
11255         if(this.form.errorReader){
11256             var rs = this.form.errorReader.read(response);
11257             var errors = [];
11258             if(rs.records){
11259                 for(var i = 0, len = rs.records.length; i < len; i++) {
11260                     var r = rs.records[i];
11261                     errors[i] = r.data;
11262                 }
11263             }
11264             if(errors.length < 1){
11265                 errors = null;
11266             }
11267             return {
11268                 success : rs.success,
11269                 errors : errors
11270             };
11271         }
11272         var ret = false;
11273         try {
11274             ret = Roo.decode(response.responseText);
11275         } catch (e) {
11276             ret = {
11277                 success: false,
11278                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11279                 errors : []
11280             };
11281         }
11282         return ret;
11283         
11284     }
11285 });
11286
11287
11288 Roo.form.Action.Load = function(form, options){
11289     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11290     this.reader = this.form.reader;
11291 };
11292
11293 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11294     type : 'load',
11295
11296     run : function(){
11297         
11298         Roo.Ajax.request(Roo.apply(
11299                 this.createCallback(), {
11300                     method:this.getMethod(),
11301                     url:this.getUrl(false),
11302                     params:this.getParams()
11303         }));
11304     },
11305
11306     success : function(response){
11307         
11308         var result = this.processResponse(response);
11309         if(result === true || !result.success || !result.data){
11310             this.failureType = Roo.form.Action.LOAD_FAILURE;
11311             this.form.afterAction(this, false);
11312             return;
11313         }
11314         this.form.clearInvalid();
11315         this.form.setValues(result.data);
11316         this.form.afterAction(this, true);
11317     },
11318
11319     handleResponse : function(response){
11320         if(this.form.reader){
11321             var rs = this.form.reader.read(response);
11322             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11323             return {
11324                 success : rs.success,
11325                 data : data
11326             };
11327         }
11328         return Roo.decode(response.responseText);
11329     }
11330 });
11331
11332 Roo.form.Action.ACTION_TYPES = {
11333     'load' : Roo.form.Action.Load,
11334     'submit' : Roo.form.Action.Submit
11335 };/*
11336  * - LGPL
11337  *
11338  * form
11339  *
11340  */
11341
11342 /**
11343  * @class Roo.bootstrap.Form
11344  * @extends Roo.bootstrap.Component
11345  * @children Roo.bootstrap.Component
11346  * Bootstrap Form class
11347  * @cfg {String} method  GET | POST (default POST)
11348  * @cfg {String} labelAlign top | left (default top)
11349  * @cfg {String} align left  | right - for navbars
11350  * @cfg {Boolean} loadMask load mask when submit (default true)
11351
11352  *
11353  * @constructor
11354  * Create a new Form
11355  * @param {Object} config The config object
11356  */
11357
11358
11359 Roo.bootstrap.Form = function(config){
11360     
11361     Roo.bootstrap.Form.superclass.constructor.call(this, config);
11362     
11363     Roo.bootstrap.Form.popover.apply();
11364     
11365     this.addEvents({
11366         /**
11367          * @event clientvalidation
11368          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11369          * @param {Form} this
11370          * @param {Boolean} valid true if the form has passed client-side validation
11371          */
11372         clientvalidation: true,
11373         /**
11374          * @event beforeaction
11375          * Fires before any action is performed. Return false to cancel the action.
11376          * @param {Form} this
11377          * @param {Action} action The action to be performed
11378          */
11379         beforeaction: true,
11380         /**
11381          * @event actionfailed
11382          * Fires when an action fails.
11383          * @param {Form} this
11384          * @param {Action} action The action that failed
11385          */
11386         actionfailed : true,
11387         /**
11388          * @event actioncomplete
11389          * Fires when an action is completed.
11390          * @param {Form} this
11391          * @param {Action} action The action that completed
11392          */
11393         actioncomplete : true
11394     });
11395 };
11396
11397 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
11398
11399      /**
11400      * @cfg {String} method
11401      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11402      */
11403     method : 'POST',
11404     /**
11405      * @cfg {String} url
11406      * The URL to use for form actions if one isn't supplied in the action options.
11407      */
11408     /**
11409      * @cfg {Boolean} fileUpload
11410      * Set to true if this form is a file upload.
11411      */
11412
11413     /**
11414      * @cfg {Object} baseParams
11415      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11416      */
11417
11418     /**
11419      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11420      */
11421     timeout: 30,
11422     /**
11423      * @cfg {Sting} align (left|right) for navbar forms
11424      */
11425     align : 'left',
11426
11427     // private
11428     activeAction : null,
11429
11430     /**
11431      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11432      * element by passing it or its id or mask the form itself by passing in true.
11433      * @type Mixed
11434      */
11435     waitMsgTarget : false,
11436
11437     loadMask : true,
11438     
11439     /**
11440      * @cfg {Boolean} errorMask (true|false) default false
11441      */
11442     errorMask : false,
11443     
11444     /**
11445      * @cfg {Number} maskOffset Default 100
11446      */
11447     maskOffset : 100,
11448     
11449     /**
11450      * @cfg {Boolean} maskBody
11451      */
11452     maskBody : false,
11453
11454     getAutoCreate : function(){
11455
11456         var cfg = {
11457             tag: 'form',
11458             method : this.method || 'POST',
11459             id : this.id || Roo.id(),
11460             cls : ''
11461         };
11462         if (this.parent().xtype.match(/^Nav/)) {
11463             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11464
11465         }
11466
11467         if (this.labelAlign == 'left' ) {
11468             cfg.cls += ' form-horizontal';
11469         }
11470
11471
11472         return cfg;
11473     },
11474     initEvents : function()
11475     {
11476         this.el.on('submit', this.onSubmit, this);
11477         // this was added as random key presses on the form where triggering form submit.
11478         this.el.on('keypress', function(e) {
11479             if (e.getCharCode() != 13) {
11480                 return true;
11481             }
11482             // we might need to allow it for textareas.. and some other items.
11483             // check e.getTarget().
11484
11485             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11486                 return true;
11487             }
11488
11489             Roo.log("keypress blocked");
11490
11491             e.preventDefault();
11492             return false;
11493         });
11494         
11495     },
11496     // private
11497     onSubmit : function(e){
11498         e.stopEvent();
11499     },
11500
11501      /**
11502      * Returns true if client-side validation on the form is successful.
11503      * @return Boolean
11504      */
11505     isValid : function(){
11506         var items = this.getItems();
11507         var valid = true;
11508         var target = false;
11509         
11510         items.each(function(f){
11511             
11512             if(f.validate()){
11513                 return;
11514             }
11515             
11516             Roo.log('invalid field: ' + f.name);
11517             
11518             valid = false;
11519
11520             if(!target && f.el.isVisible(true)){
11521                 target = f;
11522             }
11523            
11524         });
11525         
11526         if(this.errorMask && !valid){
11527             Roo.bootstrap.Form.popover.mask(this, target);
11528         }
11529         
11530         return valid;
11531     },
11532     
11533     /**
11534      * Returns true if any fields in this form have changed since their original load.
11535      * @return Boolean
11536      */
11537     isDirty : function(){
11538         var dirty = false;
11539         var items = this.getItems();
11540         items.each(function(f){
11541            if(f.isDirty()){
11542                dirty = true;
11543                return false;
11544            }
11545            return true;
11546         });
11547         return dirty;
11548     },
11549      /**
11550      * Performs a predefined action (submit or load) or custom actions you define on this form.
11551      * @param {String} actionName The name of the action type
11552      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11553      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11554      * accept other config options):
11555      * <pre>
11556 Property          Type             Description
11557 ----------------  ---------------  ----------------------------------------------------------------------------------
11558 url               String           The url for the action (defaults to the form's url)
11559 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11560 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11561 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11562                                    validate the form on the client (defaults to false)
11563      * </pre>
11564      * @return {BasicForm} this
11565      */
11566     doAction : function(action, options){
11567         if(typeof action == 'string'){
11568             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11569         }
11570         if(this.fireEvent('beforeaction', this, action) !== false){
11571             this.beforeAction(action);
11572             action.run.defer(100, action);
11573         }
11574         return this;
11575     },
11576
11577     // private
11578     beforeAction : function(action){
11579         var o = action.options;
11580         
11581         if(this.loadMask){
11582             
11583             if(this.maskBody){
11584                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11585             } else {
11586                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11587             }
11588         }
11589         // not really supported yet.. ??
11590
11591         //if(this.waitMsgTarget === true){
11592         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11593         //}else if(this.waitMsgTarget){
11594         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11595         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11596         //}else {
11597         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11598        // }
11599
11600     },
11601
11602     // private
11603     afterAction : function(action, success){
11604         this.activeAction = null;
11605         var o = action.options;
11606
11607         if(this.loadMask){
11608             
11609             if(this.maskBody){
11610                 Roo.get(document.body).unmask();
11611             } else {
11612                 this.el.unmask();
11613             }
11614         }
11615         
11616         //if(this.waitMsgTarget === true){
11617 //            this.el.unmask();
11618         //}else if(this.waitMsgTarget){
11619         //    this.waitMsgTarget.unmask();
11620         //}else{
11621         //    Roo.MessageBox.updateProgress(1);
11622         //    Roo.MessageBox.hide();
11623        // }
11624         //
11625         if(success){
11626             if(o.reset){
11627                 this.reset();
11628             }
11629             Roo.callback(o.success, o.scope, [this, action]);
11630             this.fireEvent('actioncomplete', this, action);
11631
11632         }else{
11633
11634             // failure condition..
11635             // we have a scenario where updates need confirming.
11636             // eg. if a locking scenario exists..
11637             // we look for { errors : { needs_confirm : true }} in the response.
11638             if (
11639                 (typeof(action.result) != 'undefined')  &&
11640                 (typeof(action.result.errors) != 'undefined')  &&
11641                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11642            ){
11643                 var _t = this;
11644                 Roo.log("not supported yet");
11645                  /*
11646
11647                 Roo.MessageBox.confirm(
11648                     "Change requires confirmation",
11649                     action.result.errorMsg,
11650                     function(r) {
11651                         if (r != 'yes') {
11652                             return;
11653                         }
11654                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11655                     }
11656
11657                 );
11658                 */
11659
11660
11661                 return;
11662             }
11663
11664             Roo.callback(o.failure, o.scope, [this, action]);
11665             // show an error message if no failed handler is set..
11666             if (!this.hasListener('actionfailed')) {
11667                 Roo.log("need to add dialog support");
11668                 /*
11669                 Roo.MessageBox.alert("Error",
11670                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11671                         action.result.errorMsg :
11672                         "Saving Failed, please check your entries or try again"
11673                 );
11674                 */
11675             }
11676
11677             this.fireEvent('actionfailed', this, action);
11678         }
11679
11680     },
11681     /**
11682      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11683      * @param {String} id The value to search for
11684      * @return Field
11685      */
11686     findField : function(id){
11687         var items = this.getItems();
11688         var field = items.get(id);
11689         if(!field){
11690              items.each(function(f){
11691                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11692                     field = f;
11693                     return false;
11694                 }
11695                 return true;
11696             });
11697         }
11698         return field || null;
11699     },
11700      /**
11701      * Mark fields in this form invalid in bulk.
11702      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11703      * @return {BasicForm} this
11704      */
11705     markInvalid : function(errors){
11706         if(errors instanceof Array){
11707             for(var i = 0, len = errors.length; i < len; i++){
11708                 var fieldError = errors[i];
11709                 var f = this.findField(fieldError.id);
11710                 if(f){
11711                     f.markInvalid(fieldError.msg);
11712                 }
11713             }
11714         }else{
11715             var field, id;
11716             for(id in errors){
11717                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11718                     field.markInvalid(errors[id]);
11719                 }
11720             }
11721         }
11722         //Roo.each(this.childForms || [], function (f) {
11723         //    f.markInvalid(errors);
11724         //});
11725
11726         return this;
11727     },
11728
11729     /**
11730      * Set values for fields in this form in bulk.
11731      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11732      * @return {BasicForm} this
11733      */
11734     setValues : function(values){
11735         if(values instanceof Array){ // array of objects
11736             for(var i = 0, len = values.length; i < len; i++){
11737                 var v = values[i];
11738                 var f = this.findField(v.id);
11739                 if(f){
11740                     f.setValue(v.value);
11741                     if(this.trackResetOnLoad){
11742                         f.originalValue = f.getValue();
11743                     }
11744                 }
11745             }
11746         }else{ // object hash
11747             var field, id;
11748             for(id in values){
11749                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11750
11751                     if (field.setFromData &&
11752                         field.valueField &&
11753                         field.displayField &&
11754                         // combos' with local stores can
11755                         // be queried via setValue()
11756                         // to set their value..
11757                         (field.store && !field.store.isLocal)
11758                         ) {
11759                         // it's a combo
11760                         var sd = { };
11761                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11762                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11763                         field.setFromData(sd);
11764
11765                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11766                         
11767                         field.setFromData(values);
11768                         
11769                     } else {
11770                         field.setValue(values[id]);
11771                     }
11772
11773
11774                     if(this.trackResetOnLoad){
11775                         field.originalValue = field.getValue();
11776                     }
11777                 }
11778             }
11779         }
11780
11781         //Roo.each(this.childForms || [], function (f) {
11782         //    f.setValues(values);
11783         //});
11784
11785         return this;
11786     },
11787
11788     /**
11789      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11790      * they are returned as an array.
11791      * @param {Boolean} asString
11792      * @return {Object}
11793      */
11794     getValues : function(asString){
11795         //if (this.childForms) {
11796             // copy values from the child forms
11797         //    Roo.each(this.childForms, function (f) {
11798         //        this.setValues(f.getValues());
11799         //    }, this);
11800         //}
11801
11802
11803
11804         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11805         if(asString === true){
11806             return fs;
11807         }
11808         return Roo.urlDecode(fs);
11809     },
11810
11811     /**
11812      * Returns the fields in this form as an object with key/value pairs.
11813      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11814      * @return {Object}
11815      */
11816     getFieldValues : function(with_hidden)
11817     {
11818         var items = this.getItems();
11819         var ret = {};
11820         items.each(function(f){
11821             
11822             if (!f.getName()) {
11823                 return;
11824             }
11825             
11826             var v = f.getValue();
11827             
11828             if (f.inputType =='radio') {
11829                 if (typeof(ret[f.getName()]) == 'undefined') {
11830                     ret[f.getName()] = ''; // empty..
11831                 }
11832
11833                 if (!f.el.dom.checked) {
11834                     return;
11835
11836                 }
11837                 v = f.el.dom.value;
11838
11839             }
11840             
11841             if(f.xtype == 'MoneyField'){
11842                 ret[f.currencyName] = f.getCurrency();
11843             }
11844
11845             // not sure if this supported any more..
11846             if ((typeof(v) == 'object') && f.getRawValue) {
11847                 v = f.getRawValue() ; // dates..
11848             }
11849             // combo boxes where name != hiddenName...
11850             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11851                 ret[f.name] = f.getRawValue();
11852             }
11853             ret[f.getName()] = v;
11854         });
11855
11856         return ret;
11857     },
11858
11859     /**
11860      * Clears all invalid messages in this form.
11861      * @return {BasicForm} this
11862      */
11863     clearInvalid : function(){
11864         var items = this.getItems();
11865
11866         items.each(function(f){
11867            f.clearInvalid();
11868         });
11869
11870         return this;
11871     },
11872
11873     /**
11874      * Resets this form.
11875      * @return {BasicForm} this
11876      */
11877     reset : function(){
11878         var items = this.getItems();
11879         items.each(function(f){
11880             f.reset();
11881         });
11882
11883         Roo.each(this.childForms || [], function (f) {
11884             f.reset();
11885         });
11886
11887
11888         return this;
11889     },
11890     
11891     getItems : function()
11892     {
11893         var r=new Roo.util.MixedCollection(false, function(o){
11894             return o.id || (o.id = Roo.id());
11895         });
11896         var iter = function(el) {
11897             if (el.inputEl) {
11898                 r.add(el);
11899             }
11900             if (!el.items) {
11901                 return;
11902             }
11903             Roo.each(el.items,function(e) {
11904                 iter(e);
11905             });
11906         };
11907
11908         iter(this);
11909         return r;
11910     },
11911     
11912     hideFields : function(items)
11913     {
11914         Roo.each(items, function(i){
11915             
11916             var f = this.findField(i);
11917             
11918             if(!f){
11919                 return;
11920             }
11921             
11922             f.hide();
11923             
11924         }, this);
11925     },
11926     
11927     showFields : function(items)
11928     {
11929         Roo.each(items, function(i){
11930             
11931             var f = this.findField(i);
11932             
11933             if(!f){
11934                 return;
11935             }
11936             
11937             f.show();
11938             
11939         }, this);
11940     }
11941
11942 });
11943
11944 Roo.apply(Roo.bootstrap.Form, {
11945     
11946     popover : {
11947         
11948         padding : 5,
11949         
11950         isApplied : false,
11951         
11952         isMasked : false,
11953         
11954         form : false,
11955         
11956         target : false,
11957         
11958         toolTip : false,
11959         
11960         intervalID : false,
11961         
11962         maskEl : false,
11963         
11964         apply : function()
11965         {
11966             if(this.isApplied){
11967                 return;
11968             }
11969             
11970             this.maskEl = {
11971                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11972                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11973                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11974                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11975             };
11976             
11977             this.maskEl.top.enableDisplayMode("block");
11978             this.maskEl.left.enableDisplayMode("block");
11979             this.maskEl.bottom.enableDisplayMode("block");
11980             this.maskEl.right.enableDisplayMode("block");
11981             
11982             this.toolTip = new Roo.bootstrap.Tooltip({
11983                 cls : 'roo-form-error-popover',
11984                 alignment : {
11985                     'left' : ['r-l', [-2,0], 'right'],
11986                     'right' : ['l-r', [2,0], 'left'],
11987                     'bottom' : ['tl-bl', [0,2], 'top'],
11988                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11989                 }
11990             });
11991             
11992             this.toolTip.render(Roo.get(document.body));
11993
11994             this.toolTip.el.enableDisplayMode("block");
11995             
11996             Roo.get(document.body).on('click', function(){
11997                 this.unmask();
11998             }, this);
11999             
12000             Roo.get(document.body).on('touchstart', function(){
12001                 this.unmask();
12002             }, this);
12003             
12004             this.isApplied = true
12005         },
12006         
12007         mask : function(form, target)
12008         {
12009             this.form = form;
12010             
12011             this.target = target;
12012             
12013             if(!this.form.errorMask || !target.el){
12014                 return;
12015             }
12016             
12017             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12018             
12019             Roo.log(scrollable);
12020             
12021             var ot = this.target.el.calcOffsetsTo(scrollable);
12022             
12023             var scrollTo = ot[1] - this.form.maskOffset;
12024             
12025             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12026             
12027             scrollable.scrollTo('top', scrollTo);
12028             
12029             var box = this.target.el.getBox();
12030             Roo.log(box);
12031             var zIndex = Roo.bootstrap.Modal.zIndex++;
12032
12033             
12034             this.maskEl.top.setStyle('position', 'absolute');
12035             this.maskEl.top.setStyle('z-index', zIndex);
12036             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12037             this.maskEl.top.setLeft(0);
12038             this.maskEl.top.setTop(0);
12039             this.maskEl.top.show();
12040             
12041             this.maskEl.left.setStyle('position', 'absolute');
12042             this.maskEl.left.setStyle('z-index', zIndex);
12043             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12044             this.maskEl.left.setLeft(0);
12045             this.maskEl.left.setTop(box.y - this.padding);
12046             this.maskEl.left.show();
12047
12048             this.maskEl.bottom.setStyle('position', 'absolute');
12049             this.maskEl.bottom.setStyle('z-index', zIndex);
12050             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12051             this.maskEl.bottom.setLeft(0);
12052             this.maskEl.bottom.setTop(box.bottom + this.padding);
12053             this.maskEl.bottom.show();
12054
12055             this.maskEl.right.setStyle('position', 'absolute');
12056             this.maskEl.right.setStyle('z-index', zIndex);
12057             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12058             this.maskEl.right.setLeft(box.right + this.padding);
12059             this.maskEl.right.setTop(box.y - this.padding);
12060             this.maskEl.right.show();
12061
12062             this.toolTip.bindEl = this.target.el;
12063
12064             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12065
12066             var tip = this.target.blankText;
12067
12068             if(this.target.getValue() !== '' ) {
12069                 
12070                 if (this.target.invalidText.length) {
12071                     tip = this.target.invalidText;
12072                 } else if (this.target.regexText.length){
12073                     tip = this.target.regexText;
12074                 }
12075             }
12076
12077             this.toolTip.show(tip);
12078
12079             this.intervalID = window.setInterval(function() {
12080                 Roo.bootstrap.Form.popover.unmask();
12081             }, 10000);
12082
12083             window.onwheel = function(){ return false;};
12084             
12085             (function(){ this.isMasked = true; }).defer(500, this);
12086             
12087         },
12088         
12089         unmask : function()
12090         {
12091             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12092                 return;
12093             }
12094             
12095             this.maskEl.top.setStyle('position', 'absolute');
12096             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12097             this.maskEl.top.hide();
12098
12099             this.maskEl.left.setStyle('position', 'absolute');
12100             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12101             this.maskEl.left.hide();
12102
12103             this.maskEl.bottom.setStyle('position', 'absolute');
12104             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12105             this.maskEl.bottom.hide();
12106
12107             this.maskEl.right.setStyle('position', 'absolute');
12108             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12109             this.maskEl.right.hide();
12110             
12111             this.toolTip.hide();
12112             
12113             this.toolTip.el.hide();
12114             
12115             window.onwheel = function(){ return true;};
12116             
12117             if(this.intervalID){
12118                 window.clearInterval(this.intervalID);
12119                 this.intervalID = false;
12120             }
12121             
12122             this.isMasked = false;
12123             
12124         }
12125         
12126     }
12127     
12128 });
12129
12130 /*
12131  * Based on:
12132  * Ext JS Library 1.1.1
12133  * Copyright(c) 2006-2007, Ext JS, LLC.
12134  *
12135  * Originally Released Under LGPL - original licence link has changed is not relivant.
12136  *
12137  * Fork - LGPL
12138  * <script type="text/javascript">
12139  */
12140 /**
12141  * @class Roo.form.VTypes
12142  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12143  * @singleton
12144  */
12145 Roo.form.VTypes = function(){
12146     // closure these in so they are only created once.
12147     var alpha = /^[a-zA-Z_]+$/;
12148     var alphanum = /^[a-zA-Z0-9_]+$/;
12149     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12150     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12151
12152     // All these messages and functions are configurable
12153     return {
12154         /**
12155          * The function used to validate email addresses
12156          * @param {String} value The email address
12157          */
12158         'email' : function(v){
12159             return email.test(v);
12160         },
12161         /**
12162          * The error text to display when the email validation function returns false
12163          * @type String
12164          */
12165         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12166         /**
12167          * The keystroke filter mask to be applied on email input
12168          * @type RegExp
12169          */
12170         'emailMask' : /[a-z0-9_\.\-@]/i,
12171
12172         /**
12173          * The function used to validate URLs
12174          * @param {String} value The URL
12175          */
12176         'url' : function(v){
12177             return url.test(v);
12178         },
12179         /**
12180          * The error text to display when the url validation function returns false
12181          * @type String
12182          */
12183         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12184         
12185         /**
12186          * The function used to validate alpha values
12187          * @param {String} value The value
12188          */
12189         'alpha' : function(v){
12190             return alpha.test(v);
12191         },
12192         /**
12193          * The error text to display when the alpha validation function returns false
12194          * @type String
12195          */
12196         'alphaText' : 'This field should only contain letters and _',
12197         /**
12198          * The keystroke filter mask to be applied on alpha input
12199          * @type RegExp
12200          */
12201         'alphaMask' : /[a-z_]/i,
12202
12203         /**
12204          * The function used to validate alphanumeric values
12205          * @param {String} value The value
12206          */
12207         'alphanum' : function(v){
12208             return alphanum.test(v);
12209         },
12210         /**
12211          * The error text to display when the alphanumeric validation function returns false
12212          * @type String
12213          */
12214         'alphanumText' : 'This field should only contain letters, numbers and _',
12215         /**
12216          * The keystroke filter mask to be applied on alphanumeric input
12217          * @type RegExp
12218          */
12219         'alphanumMask' : /[a-z0-9_]/i
12220     };
12221 }();/*
12222  * - LGPL
12223  *
12224  * Input
12225  * 
12226  */
12227
12228 /**
12229  * @class Roo.bootstrap.Input
12230  * @extends Roo.bootstrap.Component
12231  * Bootstrap Input class
12232  * @cfg {Boolean} disabled is it disabled
12233  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12234  * @cfg {String} name name of the input
12235  * @cfg {string} fieldLabel - the label associated
12236  * @cfg {string} placeholder - placeholder to put in text.
12237  * @cfg {string} before - input group add on before
12238  * @cfg {string} after - input group add on after
12239  * @cfg {string} size - (lg|sm) or leave empty..
12240  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12241  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12242  * @cfg {Number} md colspan out of 12 for computer-sized screens
12243  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12244  * @cfg {string} value default value of the input
12245  * @cfg {Number} labelWidth set the width of label 
12246  * @cfg {Number} labellg set the width of label (1-12)
12247  * @cfg {Number} labelmd set the width of label (1-12)
12248  * @cfg {Number} labelsm set the width of label (1-12)
12249  * @cfg {Number} labelxs set the width of label (1-12)
12250  * @cfg {String} labelAlign (top|left)
12251  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12252  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12253  * @cfg {String} indicatorpos (left|right) default left
12254  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12255  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12256  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12257  * @cfg {Roo.bootstrap.Button} before Button to show before
12258  * @cfg {Roo.bootstrap.Button} afterButton to show before
12259  * @cfg {String} align (left|center|right) Default left
12260  * @cfg {Boolean} forceFeedback (true|false) Default false
12261  * 
12262  * @constructor
12263  * Create a new Input
12264  * @param {Object} config The config object
12265  */
12266
12267 Roo.bootstrap.Input = function(config){
12268     
12269     Roo.bootstrap.Input.superclass.constructor.call(this, config);
12270     
12271     this.addEvents({
12272         /**
12273          * @event focus
12274          * Fires when this field receives input focus.
12275          * @param {Roo.form.Field} this
12276          */
12277         focus : true,
12278         /**
12279          * @event blur
12280          * Fires when this field loses input focus.
12281          * @param {Roo.form.Field} this
12282          */
12283         blur : true,
12284         /**
12285          * @event specialkey
12286          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12287          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12288          * @param {Roo.form.Field} this
12289          * @param {Roo.EventObject} e The event object
12290          */
12291         specialkey : true,
12292         /**
12293          * @event change
12294          * Fires just before the field blurs if the field value has changed.
12295          * @param {Roo.form.Field} this
12296          * @param {Mixed} newValue The new value
12297          * @param {Mixed} oldValue The original value
12298          */
12299         change : true,
12300         /**
12301          * @event invalid
12302          * Fires after the field has been marked as invalid.
12303          * @param {Roo.form.Field} this
12304          * @param {String} msg The validation message
12305          */
12306         invalid : true,
12307         /**
12308          * @event valid
12309          * Fires after the field has been validated with no errors.
12310          * @param {Roo.form.Field} this
12311          */
12312         valid : true,
12313          /**
12314          * @event keyup
12315          * Fires after the key up
12316          * @param {Roo.form.Field} this
12317          * @param {Roo.EventObject}  e The event Object
12318          */
12319         keyup : true,
12320         /**
12321          * @event paste
12322          * Fires after the user pastes into input
12323          * @param {Roo.form.Field} this
12324          * @param {Roo.EventObject}  e The event Object
12325          */
12326         paste : true
12327     });
12328 };
12329
12330 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
12331      /**
12332      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12333       automatic validation (defaults to "keyup").
12334      */
12335     validationEvent : "keyup",
12336      /**
12337      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12338      */
12339     validateOnBlur : true,
12340     /**
12341      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12342      */
12343     validationDelay : 250,
12344      /**
12345      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12346      */
12347     focusClass : "x-form-focus",  // not needed???
12348     
12349        
12350     /**
12351      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12352      */
12353     invalidClass : "has-warning",
12354     
12355     /**
12356      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12357      */
12358     validClass : "has-success",
12359     
12360     /**
12361      * @cfg {Boolean} hasFeedback (true|false) default true
12362      */
12363     hasFeedback : true,
12364     
12365     /**
12366      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12367      */
12368     invalidFeedbackClass : "glyphicon-warning-sign",
12369     
12370     /**
12371      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12372      */
12373     validFeedbackClass : "glyphicon-ok",
12374     
12375     /**
12376      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12377      */
12378     selectOnFocus : false,
12379     
12380      /**
12381      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12382      */
12383     maskRe : null,
12384        /**
12385      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12386      */
12387     vtype : null,
12388     
12389       /**
12390      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12391      */
12392     disableKeyFilter : false,
12393     
12394        /**
12395      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12396      */
12397     disabled : false,
12398      /**
12399      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12400      */
12401     allowBlank : true,
12402     /**
12403      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12404      */
12405     blankText : "Please complete this mandatory field",
12406     
12407      /**
12408      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12409      */
12410     minLength : 0,
12411     /**
12412      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12413      */
12414     maxLength : Number.MAX_VALUE,
12415     /**
12416      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12417      */
12418     minLengthText : "The minimum length for this field is {0}",
12419     /**
12420      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12421      */
12422     maxLengthText : "The maximum length for this field is {0}",
12423   
12424     
12425     /**
12426      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12427      * If available, this function will be called only after the basic validators all return true, and will be passed the
12428      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12429      */
12430     validator : null,
12431     /**
12432      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12433      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12434      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12435      */
12436     regex : null,
12437     /**
12438      * @cfg {String} regexText -- Depricated - use Invalid Text
12439      */
12440     regexText : "",
12441     
12442     /**
12443      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12444      */
12445     invalidText : "",
12446     
12447     
12448     
12449     autocomplete: false,
12450     
12451     
12452     fieldLabel : '',
12453     inputType : 'text',
12454     
12455     name : false,
12456     placeholder: false,
12457     before : false,
12458     after : false,
12459     size : false,
12460     hasFocus : false,
12461     preventMark: false,
12462     isFormField : true,
12463     value : '',
12464     labelWidth : 2,
12465     labelAlign : false,
12466     readOnly : false,
12467     align : false,
12468     formatedValue : false,
12469     forceFeedback : false,
12470     
12471     indicatorpos : 'left',
12472     
12473     labellg : 0,
12474     labelmd : 0,
12475     labelsm : 0,
12476     labelxs : 0,
12477     
12478     capture : '',
12479     accept : '',
12480     
12481     parentLabelAlign : function()
12482     {
12483         var parent = this;
12484         while (parent.parent()) {
12485             parent = parent.parent();
12486             if (typeof(parent.labelAlign) !='undefined') {
12487                 return parent.labelAlign;
12488             }
12489         }
12490         return 'left';
12491         
12492     },
12493     
12494     getAutoCreate : function()
12495     {
12496         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12497         
12498         var id = Roo.id();
12499         
12500         var cfg = {};
12501         
12502         if(this.inputType != 'hidden'){
12503             cfg.cls = 'form-group' //input-group
12504         }
12505         
12506         var input =  {
12507             tag: 'input',
12508             id : id,
12509             type : this.inputType,
12510             value : this.value,
12511             cls : 'form-control',
12512             placeholder : this.placeholder || '',
12513             autocomplete : this.autocomplete || 'new-password'
12514         };
12515         if (this.inputType == 'file') {
12516             input.style = 'overflow:hidden'; // why not in CSS?
12517         }
12518         
12519         if(this.capture.length){
12520             input.capture = this.capture;
12521         }
12522         
12523         if(this.accept.length){
12524             input.accept = this.accept + "/*";
12525         }
12526         
12527         if(this.align){
12528             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12529         }
12530         
12531         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12532             input.maxLength = this.maxLength;
12533         }
12534         
12535         if (this.disabled) {
12536             input.disabled=true;
12537         }
12538         
12539         if (this.readOnly) {
12540             input.readonly=true;
12541         }
12542         
12543         if (this.name) {
12544             input.name = this.name;
12545         }
12546         
12547         if (this.size) {
12548             input.cls += ' input-' + this.size;
12549         }
12550         
12551         var settings=this;
12552         ['xs','sm','md','lg'].map(function(size){
12553             if (settings[size]) {
12554                 cfg.cls += ' col-' + size + '-' + settings[size];
12555             }
12556         });
12557         
12558         var inputblock = input;
12559         
12560         var feedback = {
12561             tag: 'span',
12562             cls: 'glyphicon form-control-feedback'
12563         };
12564             
12565         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12566             
12567             inputblock = {
12568                 cls : 'has-feedback',
12569                 cn :  [
12570                     input,
12571                     feedback
12572                 ] 
12573             };  
12574         }
12575         
12576         if (this.before || this.after) {
12577             
12578             inputblock = {
12579                 cls : 'input-group',
12580                 cn :  [] 
12581             };
12582             
12583             if (this.before && typeof(this.before) == 'string') {
12584                 
12585                 inputblock.cn.push({
12586                     tag :'span',
12587                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12588                     html : this.before
12589                 });
12590             }
12591             if (this.before && typeof(this.before) == 'object') {
12592                 this.before = Roo.factory(this.before);
12593                 
12594                 inputblock.cn.push({
12595                     tag :'span',
12596                     cls : 'roo-input-before input-group-prepend   input-group-' +
12597                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12598                 });
12599             }
12600             
12601             inputblock.cn.push(input);
12602             
12603             if (this.after && typeof(this.after) == 'string') {
12604                 inputblock.cn.push({
12605                     tag :'span',
12606                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12607                     html : this.after
12608                 });
12609             }
12610             if (this.after && typeof(this.after) == 'object') {
12611                 this.after = Roo.factory(this.after);
12612                 
12613                 inputblock.cn.push({
12614                     tag :'span',
12615                     cls : 'roo-input-after input-group-append  input-group-' +
12616                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12617                 });
12618             }
12619             
12620             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12621                 inputblock.cls += ' has-feedback';
12622                 inputblock.cn.push(feedback);
12623             }
12624         };
12625         var indicator = {
12626             tag : 'i',
12627             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12628             tooltip : 'This field is required'
12629         };
12630         if (this.allowBlank ) {
12631             indicator.style = this.allowBlank ? ' display:none' : '';
12632         }
12633         if (align ==='left' && this.fieldLabel.length) {
12634             
12635             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12636             
12637             cfg.cn = [
12638                 indicator,
12639                 {
12640                     tag: 'label',
12641                     'for' :  id,
12642                     cls : 'control-label col-form-label',
12643                     html : this.fieldLabel
12644
12645                 },
12646                 {
12647                     cls : "", 
12648                     cn: [
12649                         inputblock
12650                     ]
12651                 }
12652             ];
12653             
12654             var labelCfg = cfg.cn[1];
12655             var contentCfg = cfg.cn[2];
12656             
12657             if(this.indicatorpos == 'right'){
12658                 cfg.cn = [
12659                     {
12660                         tag: 'label',
12661                         'for' :  id,
12662                         cls : 'control-label col-form-label',
12663                         cn : [
12664                             {
12665                                 tag : 'span',
12666                                 html : this.fieldLabel
12667                             },
12668                             indicator
12669                         ]
12670                     },
12671                     {
12672                         cls : "",
12673                         cn: [
12674                             inputblock
12675                         ]
12676                     }
12677
12678                 ];
12679                 
12680                 labelCfg = cfg.cn[0];
12681                 contentCfg = cfg.cn[1];
12682             
12683             }
12684             
12685             if(this.labelWidth > 12){
12686                 labelCfg.style = "width: " + this.labelWidth + 'px';
12687             }
12688             
12689             if(this.labelWidth < 13 && this.labelmd == 0){
12690                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12691             }
12692             
12693             if(this.labellg > 0){
12694                 labelCfg.cls += ' col-lg-' + this.labellg;
12695                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12696             }
12697             
12698             if(this.labelmd > 0){
12699                 labelCfg.cls += ' col-md-' + this.labelmd;
12700                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12701             }
12702             
12703             if(this.labelsm > 0){
12704                 labelCfg.cls += ' col-sm-' + this.labelsm;
12705                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12706             }
12707             
12708             if(this.labelxs > 0){
12709                 labelCfg.cls += ' col-xs-' + this.labelxs;
12710                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12711             }
12712             
12713             
12714         } else if ( this.fieldLabel.length) {
12715                 
12716             
12717             
12718             cfg.cn = [
12719                 {
12720                     tag : 'i',
12721                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12722                     tooltip : 'This field is required',
12723                     style : this.allowBlank ? ' display:none' : '' 
12724                 },
12725                 {
12726                     tag: 'label',
12727                    //cls : 'input-group-addon',
12728                     html : this.fieldLabel
12729
12730                 },
12731
12732                inputblock
12733
12734            ];
12735            
12736            if(this.indicatorpos == 'right'){
12737        
12738                 cfg.cn = [
12739                     {
12740                         tag: 'label',
12741                        //cls : 'input-group-addon',
12742                         html : this.fieldLabel
12743
12744                     },
12745                     {
12746                         tag : 'i',
12747                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12748                         tooltip : 'This field is required',
12749                         style : this.allowBlank ? ' display:none' : '' 
12750                     },
12751
12752                    inputblock
12753
12754                ];
12755
12756             }
12757
12758         } else {
12759             
12760             cfg.cn = [
12761
12762                     inputblock
12763
12764             ];
12765                 
12766                 
12767         };
12768         
12769         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12770            cfg.cls += ' navbar-form';
12771         }
12772         
12773         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12774             // on BS4 we do this only if not form 
12775             cfg.cls += ' navbar-form';
12776             cfg.tag = 'li';
12777         }
12778         
12779         return cfg;
12780         
12781     },
12782     /**
12783      * return the real input element.
12784      */
12785     inputEl: function ()
12786     {
12787         return this.el.select('input.form-control',true).first();
12788     },
12789     
12790     tooltipEl : function()
12791     {
12792         return this.inputEl();
12793     },
12794     
12795     indicatorEl : function()
12796     {
12797         if (Roo.bootstrap.version == 4) {
12798             return false; // not enabled in v4 yet.
12799         }
12800         
12801         var indicator = this.el.select('i.roo-required-indicator',true).first();
12802         
12803         if(!indicator){
12804             return false;
12805         }
12806         
12807         return indicator;
12808         
12809     },
12810     
12811     setDisabled : function(v)
12812     {
12813         var i  = this.inputEl().dom;
12814         if (!v) {
12815             i.removeAttribute('disabled');
12816             return;
12817             
12818         }
12819         i.setAttribute('disabled','true');
12820     },
12821     initEvents : function()
12822     {
12823           
12824         this.inputEl().on("keydown" , this.fireKey,  this);
12825         this.inputEl().on("focus", this.onFocus,  this);
12826         this.inputEl().on("blur", this.onBlur,  this);
12827         
12828         this.inputEl().relayEvent('keyup', this);
12829         this.inputEl().relayEvent('paste', this);
12830         
12831         this.indicator = this.indicatorEl();
12832         
12833         if(this.indicator){
12834             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12835         }
12836  
12837         // reference to original value for reset
12838         this.originalValue = this.getValue();
12839         //Roo.form.TextField.superclass.initEvents.call(this);
12840         if(this.validationEvent == 'keyup'){
12841             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12842             this.inputEl().on('keyup', this.filterValidation, this);
12843         }
12844         else if(this.validationEvent !== false){
12845             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12846         }
12847         
12848         if(this.selectOnFocus){
12849             this.on("focus", this.preFocus, this);
12850             
12851         }
12852         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12853             this.inputEl().on("keypress", this.filterKeys, this);
12854         } else {
12855             this.inputEl().relayEvent('keypress', this);
12856         }
12857        /* if(this.grow){
12858             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12859             this.el.on("click", this.autoSize,  this);
12860         }
12861         */
12862         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12863             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12864         }
12865         
12866         if (typeof(this.before) == 'object') {
12867             this.before.render(this.el.select('.roo-input-before',true).first());
12868         }
12869         if (typeof(this.after) == 'object') {
12870             this.after.render(this.el.select('.roo-input-after',true).first());
12871         }
12872         
12873         this.inputEl().on('change', this.onChange, this);
12874         
12875     },
12876     filterValidation : function(e){
12877         if(!e.isNavKeyPress()){
12878             this.validationTask.delay(this.validationDelay);
12879         }
12880     },
12881      /**
12882      * Validates the field value
12883      * @return {Boolean} True if the value is valid, else false
12884      */
12885     validate : function(){
12886         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12887         if(this.disabled || this.validateValue(this.getRawValue())){
12888             this.markValid();
12889             return true;
12890         }
12891         
12892         this.markInvalid();
12893         return false;
12894     },
12895     
12896     
12897     /**
12898      * Validates a value according to the field's validation rules and marks the field as invalid
12899      * if the validation fails
12900      * @param {Mixed} value The value to validate
12901      * @return {Boolean} True if the value is valid, else false
12902      */
12903     validateValue : function(value)
12904     {
12905         if(this.getVisibilityEl().hasClass('hidden')){
12906             return true;
12907         }
12908         
12909         if(value.length < 1)  { // if it's blank
12910             if(this.allowBlank){
12911                 return true;
12912             }
12913             return false;
12914         }
12915         
12916         if(value.length < this.minLength){
12917             return false;
12918         }
12919         if(value.length > this.maxLength){
12920             return false;
12921         }
12922         if(this.vtype){
12923             var vt = Roo.form.VTypes;
12924             if(!vt[this.vtype](value, this)){
12925                 return false;
12926             }
12927         }
12928         if(typeof this.validator == "function"){
12929             var msg = this.validator(value);
12930             if(msg !== true){
12931                 return false;
12932             }
12933             if (typeof(msg) == 'string') {
12934                 this.invalidText = msg;
12935             }
12936         }
12937         
12938         if(this.regex && !this.regex.test(value)){
12939             return false;
12940         }
12941         
12942         return true;
12943     },
12944     
12945      // private
12946     fireKey : function(e){
12947         //Roo.log('field ' + e.getKey());
12948         if(e.isNavKeyPress()){
12949             this.fireEvent("specialkey", this, e);
12950         }
12951     },
12952     focus : function (selectText){
12953         if(this.rendered){
12954             this.inputEl().focus();
12955             if(selectText === true){
12956                 this.inputEl().dom.select();
12957             }
12958         }
12959         return this;
12960     } ,
12961     
12962     onFocus : function(){
12963         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12964            // this.el.addClass(this.focusClass);
12965         }
12966         if(!this.hasFocus){
12967             this.hasFocus = true;
12968             this.startValue = this.getValue();
12969             this.fireEvent("focus", this);
12970         }
12971     },
12972     
12973     beforeBlur : Roo.emptyFn,
12974
12975     
12976     // private
12977     onBlur : function(){
12978         this.beforeBlur();
12979         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12980             //this.el.removeClass(this.focusClass);
12981         }
12982         this.hasFocus = false;
12983         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12984             this.validate();
12985         }
12986         var v = this.getValue();
12987         if(String(v) !== String(this.startValue)){
12988             this.fireEvent('change', this, v, this.startValue);
12989         }
12990         this.fireEvent("blur", this);
12991     },
12992     
12993     onChange : function(e)
12994     {
12995         var v = this.getValue();
12996         if(String(v) !== String(this.startValue)){
12997             this.fireEvent('change', this, v, this.startValue);
12998         }
12999         
13000     },
13001     
13002     /**
13003      * Resets the current field value to the originally loaded value and clears any validation messages
13004      */
13005     reset : function(){
13006         this.setValue(this.originalValue);
13007         this.validate();
13008     },
13009      /**
13010      * Returns the name of the field
13011      * @return {Mixed} name The name field
13012      */
13013     getName: function(){
13014         return this.name;
13015     },
13016      /**
13017      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13018      * @return {Mixed} value The field value
13019      */
13020     getValue : function(){
13021         
13022         var v = this.inputEl().getValue();
13023         
13024         return v;
13025     },
13026     /**
13027      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13028      * @return {Mixed} value The field value
13029      */
13030     getRawValue : function(){
13031         var v = this.inputEl().getValue();
13032         
13033         return v;
13034     },
13035     
13036     /**
13037      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13038      * @param {Mixed} value The value to set
13039      */
13040     setRawValue : function(v){
13041         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13042     },
13043     
13044     selectText : function(start, end){
13045         var v = this.getRawValue();
13046         if(v.length > 0){
13047             start = start === undefined ? 0 : start;
13048             end = end === undefined ? v.length : end;
13049             var d = this.inputEl().dom;
13050             if(d.setSelectionRange){
13051                 d.setSelectionRange(start, end);
13052             }else if(d.createTextRange){
13053                 var range = d.createTextRange();
13054                 range.moveStart("character", start);
13055                 range.moveEnd("character", v.length-end);
13056                 range.select();
13057             }
13058         }
13059     },
13060     
13061     /**
13062      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13063      * @param {Mixed} value The value to set
13064      */
13065     setValue : function(v){
13066         this.value = v;
13067         if(this.rendered){
13068             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13069             this.validate();
13070         }
13071     },
13072     
13073     /*
13074     processValue : function(value){
13075         if(this.stripCharsRe){
13076             var newValue = value.replace(this.stripCharsRe, '');
13077             if(newValue !== value){
13078                 this.setRawValue(newValue);
13079                 return newValue;
13080             }
13081         }
13082         return value;
13083     },
13084   */
13085     preFocus : function(){
13086         
13087         if(this.selectOnFocus){
13088             this.inputEl().dom.select();
13089         }
13090     },
13091     filterKeys : function(e){
13092         var k = e.getKey();
13093         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13094             return;
13095         }
13096         var c = e.getCharCode(), cc = String.fromCharCode(c);
13097         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13098             return;
13099         }
13100         if(!this.maskRe.test(cc)){
13101             e.stopEvent();
13102         }
13103     },
13104      /**
13105      * Clear any invalid styles/messages for this field
13106      */
13107     clearInvalid : function(){
13108         
13109         if(!this.el || this.preventMark){ // not rendered
13110             return;
13111         }
13112         
13113         
13114         this.el.removeClass([this.invalidClass, 'is-invalid']);
13115         
13116         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13117             
13118             var feedback = this.el.select('.form-control-feedback', true).first();
13119             
13120             if(feedback){
13121                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13122             }
13123             
13124         }
13125         
13126         if(this.indicator){
13127             this.indicator.removeClass('visible');
13128             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13129         }
13130         
13131         this.fireEvent('valid', this);
13132     },
13133     
13134      /**
13135      * Mark this field as valid
13136      */
13137     markValid : function()
13138     {
13139         if(!this.el  || this.preventMark){ // not rendered...
13140             return;
13141         }
13142         
13143         this.el.removeClass([this.invalidClass, this.validClass]);
13144         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13145
13146         var feedback = this.el.select('.form-control-feedback', true).first();
13147             
13148         if(feedback){
13149             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13150         }
13151         
13152         if(this.indicator){
13153             this.indicator.removeClass('visible');
13154             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13155         }
13156         
13157         if(this.disabled){
13158             return;
13159         }
13160         
13161            
13162         if(this.allowBlank && !this.getRawValue().length){
13163             return;
13164         }
13165         if (Roo.bootstrap.version == 3) {
13166             this.el.addClass(this.validClass);
13167         } else {
13168             this.inputEl().addClass('is-valid');
13169         }
13170
13171         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13172             
13173             var feedback = this.el.select('.form-control-feedback', true).first();
13174             
13175             if(feedback){
13176                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13177                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13178             }
13179             
13180         }
13181         
13182         this.fireEvent('valid', this);
13183     },
13184     
13185      /**
13186      * Mark this field as invalid
13187      * @param {String} msg The validation message
13188      */
13189     markInvalid : function(msg)
13190     {
13191         if(!this.el  || this.preventMark){ // not rendered
13192             return;
13193         }
13194         
13195         this.el.removeClass([this.invalidClass, this.validClass]);
13196         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13197         
13198         var feedback = this.el.select('.form-control-feedback', true).first();
13199             
13200         if(feedback){
13201             this.el.select('.form-control-feedback', true).first().removeClass(
13202                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13203         }
13204
13205         if(this.disabled){
13206             return;
13207         }
13208         
13209         if(this.allowBlank && !this.getRawValue().length){
13210             return;
13211         }
13212         
13213         if(this.indicator){
13214             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13215             this.indicator.addClass('visible');
13216         }
13217         if (Roo.bootstrap.version == 3) {
13218             this.el.addClass(this.invalidClass);
13219         } else {
13220             this.inputEl().addClass('is-invalid');
13221         }
13222         
13223         
13224         
13225         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13226             
13227             var feedback = this.el.select('.form-control-feedback', true).first();
13228             
13229             if(feedback){
13230                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13231                 
13232                 if(this.getValue().length || this.forceFeedback){
13233                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13234                 }
13235                 
13236             }
13237             
13238         }
13239         
13240         this.fireEvent('invalid', this, msg);
13241     },
13242     // private
13243     SafariOnKeyDown : function(event)
13244     {
13245         // this is a workaround for a password hang bug on chrome/ webkit.
13246         if (this.inputEl().dom.type != 'password') {
13247             return;
13248         }
13249         
13250         var isSelectAll = false;
13251         
13252         if(this.inputEl().dom.selectionEnd > 0){
13253             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13254         }
13255         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13256             event.preventDefault();
13257             this.setValue('');
13258             return;
13259         }
13260         
13261         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13262             
13263             event.preventDefault();
13264             // this is very hacky as keydown always get's upper case.
13265             //
13266             var cc = String.fromCharCode(event.getCharCode());
13267             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13268             
13269         }
13270     },
13271     adjustWidth : function(tag, w){
13272         tag = tag.toLowerCase();
13273         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13274             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13275                 if(tag == 'input'){
13276                     return w + 2;
13277                 }
13278                 if(tag == 'textarea'){
13279                     return w-2;
13280                 }
13281             }else if(Roo.isOpera){
13282                 if(tag == 'input'){
13283                     return w + 2;
13284                 }
13285                 if(tag == 'textarea'){
13286                     return w-2;
13287                 }
13288             }
13289         }
13290         return w;
13291     },
13292     
13293     setFieldLabel : function(v)
13294     {
13295         if(!this.rendered){
13296             return;
13297         }
13298         
13299         if(this.indicatorEl()){
13300             var ar = this.el.select('label > span',true);
13301             
13302             if (ar.elements.length) {
13303                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13304                 this.fieldLabel = v;
13305                 return;
13306             }
13307             
13308             var br = this.el.select('label',true);
13309             
13310             if(br.elements.length) {
13311                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13312                 this.fieldLabel = v;
13313                 return;
13314             }
13315             
13316             Roo.log('Cannot Found any of label > span || label in input');
13317             return;
13318         }
13319         
13320         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13321         this.fieldLabel = v;
13322         
13323         
13324     }
13325 });
13326
13327  
13328 /*
13329  * - LGPL
13330  *
13331  * Input
13332  * 
13333  */
13334
13335 /**
13336  * @class Roo.bootstrap.TextArea
13337  * @extends Roo.bootstrap.Input
13338  * Bootstrap TextArea class
13339  * @cfg {Number} cols Specifies the visible width of a text area
13340  * @cfg {Number} rows Specifies the visible number of lines in a text area
13341  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13342  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13343  * @cfg {string} html text
13344  * 
13345  * @constructor
13346  * Create a new TextArea
13347  * @param {Object} config The config object
13348  */
13349
13350 Roo.bootstrap.TextArea = function(config){
13351     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
13352    
13353 };
13354
13355 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
13356      
13357     cols : false,
13358     rows : 5,
13359     readOnly : false,
13360     warp : 'soft',
13361     resize : false,
13362     value: false,
13363     html: false,
13364     
13365     getAutoCreate : function(){
13366         
13367         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13368         
13369         var id = Roo.id();
13370         
13371         var cfg = {};
13372         
13373         if(this.inputType != 'hidden'){
13374             cfg.cls = 'form-group' //input-group
13375         }
13376         
13377         var input =  {
13378             tag: 'textarea',
13379             id : id,
13380             warp : this.warp,
13381             rows : this.rows,
13382             value : this.value || '',
13383             html: this.html || '',
13384             cls : 'form-control',
13385             placeholder : this.placeholder || '' 
13386             
13387         };
13388         
13389         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13390             input.maxLength = this.maxLength;
13391         }
13392         
13393         if(this.resize){
13394             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13395         }
13396         
13397         if(this.cols){
13398             input.cols = this.cols;
13399         }
13400         
13401         if (this.readOnly) {
13402             input.readonly = true;
13403         }
13404         
13405         if (this.name) {
13406             input.name = this.name;
13407         }
13408         
13409         if (this.size) {
13410             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13411         }
13412         
13413         var settings=this;
13414         ['xs','sm','md','lg'].map(function(size){
13415             if (settings[size]) {
13416                 cfg.cls += ' col-' + size + '-' + settings[size];
13417             }
13418         });
13419         
13420         var inputblock = input;
13421         
13422         if(this.hasFeedback && !this.allowBlank){
13423             
13424             var feedback = {
13425                 tag: 'span',
13426                 cls: 'glyphicon form-control-feedback'
13427             };
13428
13429             inputblock = {
13430                 cls : 'has-feedback',
13431                 cn :  [
13432                     input,
13433                     feedback
13434                 ] 
13435             };  
13436         }
13437         
13438         
13439         if (this.before || this.after) {
13440             
13441             inputblock = {
13442                 cls : 'input-group',
13443                 cn :  [] 
13444             };
13445             if (this.before) {
13446                 inputblock.cn.push({
13447                     tag :'span',
13448                     cls : 'input-group-addon',
13449                     html : this.before
13450                 });
13451             }
13452             
13453             inputblock.cn.push(input);
13454             
13455             if(this.hasFeedback && !this.allowBlank){
13456                 inputblock.cls += ' has-feedback';
13457                 inputblock.cn.push(feedback);
13458             }
13459             
13460             if (this.after) {
13461                 inputblock.cn.push({
13462                     tag :'span',
13463                     cls : 'input-group-addon',
13464                     html : this.after
13465                 });
13466             }
13467             
13468         }
13469         
13470         if (align ==='left' && this.fieldLabel.length) {
13471             cfg.cn = [
13472                 {
13473                     tag: 'label',
13474                     'for' :  id,
13475                     cls : 'control-label',
13476                     html : this.fieldLabel
13477                 },
13478                 {
13479                     cls : "",
13480                     cn: [
13481                         inputblock
13482                     ]
13483                 }
13484
13485             ];
13486             
13487             if(this.labelWidth > 12){
13488                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13489             }
13490
13491             if(this.labelWidth < 13 && this.labelmd == 0){
13492                 this.labelmd = this.labelWidth;
13493             }
13494
13495             if(this.labellg > 0){
13496                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13497                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13498             }
13499
13500             if(this.labelmd > 0){
13501                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13502                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13503             }
13504
13505             if(this.labelsm > 0){
13506                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13507                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13508             }
13509
13510             if(this.labelxs > 0){
13511                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13512                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13513             }
13514             
13515         } else if ( this.fieldLabel.length) {
13516             cfg.cn = [
13517
13518                {
13519                    tag: 'label',
13520                    //cls : 'input-group-addon',
13521                    html : this.fieldLabel
13522
13523                },
13524
13525                inputblock
13526
13527            ];
13528
13529         } else {
13530
13531             cfg.cn = [
13532
13533                 inputblock
13534
13535             ];
13536                 
13537         }
13538         
13539         if (this.disabled) {
13540             input.disabled=true;
13541         }
13542         
13543         return cfg;
13544         
13545     },
13546     /**
13547      * return the real textarea element.
13548      */
13549     inputEl: function ()
13550     {
13551         return this.el.select('textarea.form-control',true).first();
13552     },
13553     
13554     /**
13555      * Clear any invalid styles/messages for this field
13556      */
13557     clearInvalid : function()
13558     {
13559         
13560         if(!this.el || this.preventMark){ // not rendered
13561             return;
13562         }
13563         
13564         var label = this.el.select('label', true).first();
13565         var icon = this.el.select('i.fa-star', true).first();
13566         
13567         if(label && icon){
13568             icon.remove();
13569         }
13570         this.el.removeClass( this.validClass);
13571         this.inputEl().removeClass('is-invalid');
13572          
13573         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13574             
13575             var feedback = this.el.select('.form-control-feedback', true).first();
13576             
13577             if(feedback){
13578                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13579             }
13580             
13581         }
13582         
13583         this.fireEvent('valid', this);
13584     },
13585     
13586      /**
13587      * Mark this field as valid
13588      */
13589     markValid : function()
13590     {
13591         if(!this.el  || this.preventMark){ // not rendered
13592             return;
13593         }
13594         
13595         this.el.removeClass([this.invalidClass, this.validClass]);
13596         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13597         
13598         var feedback = this.el.select('.form-control-feedback', true).first();
13599             
13600         if(feedback){
13601             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13602         }
13603
13604         if(this.disabled || this.allowBlank){
13605             return;
13606         }
13607         
13608         var label = this.el.select('label', true).first();
13609         var icon = this.el.select('i.fa-star', true).first();
13610         
13611         if(label && icon){
13612             icon.remove();
13613         }
13614         if (Roo.bootstrap.version == 3) {
13615             this.el.addClass(this.validClass);
13616         } else {
13617             this.inputEl().addClass('is-valid');
13618         }
13619         
13620         
13621         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13622             
13623             var feedback = this.el.select('.form-control-feedback', true).first();
13624             
13625             if(feedback){
13626                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13627                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13628             }
13629             
13630         }
13631         
13632         this.fireEvent('valid', this);
13633     },
13634     
13635      /**
13636      * Mark this field as invalid
13637      * @param {String} msg The validation message
13638      */
13639     markInvalid : function(msg)
13640     {
13641         if(!this.el  || this.preventMark){ // not rendered
13642             return;
13643         }
13644         
13645         this.el.removeClass([this.invalidClass, this.validClass]);
13646         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13647         
13648         var feedback = this.el.select('.form-control-feedback', true).first();
13649             
13650         if(feedback){
13651             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13652         }
13653
13654         if(this.disabled || this.allowBlank){
13655             return;
13656         }
13657         
13658         var label = this.el.select('label', true).first();
13659         var icon = this.el.select('i.fa-star', true).first();
13660         
13661         if(!this.getValue().length && label && !icon){
13662             this.el.createChild({
13663                 tag : 'i',
13664                 cls : 'text-danger fa fa-lg fa-star',
13665                 tooltip : 'This field is required',
13666                 style : 'margin-right:5px;'
13667             }, label, true);
13668         }
13669         
13670         if (Roo.bootstrap.version == 3) {
13671             this.el.addClass(this.invalidClass);
13672         } else {
13673             this.inputEl().addClass('is-invalid');
13674         }
13675         
13676         // fixme ... this may be depricated need to test..
13677         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13678             
13679             var feedback = this.el.select('.form-control-feedback', true).first();
13680             
13681             if(feedback){
13682                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13683                 
13684                 if(this.getValue().length || this.forceFeedback){
13685                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13686                 }
13687                 
13688             }
13689             
13690         }
13691         
13692         this.fireEvent('invalid', this, msg);
13693     }
13694 });
13695
13696  
13697 /*
13698  * - LGPL
13699  *
13700  * trigger field - base class for combo..
13701  * 
13702  */
13703  
13704 /**
13705  * @class Roo.bootstrap.TriggerField
13706  * @extends Roo.bootstrap.Input
13707  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13708  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13709  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13710  * for which you can provide a custom implementation.  For example:
13711  * <pre><code>
13712 var trigger = new Roo.bootstrap.TriggerField();
13713 trigger.onTriggerClick = myTriggerFn;
13714 trigger.applyTo('my-field');
13715 </code></pre>
13716  *
13717  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13718  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13719  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13720  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13721  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13722
13723  * @constructor
13724  * Create a new TriggerField.
13725  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13726  * to the base TextField)
13727  */
13728 Roo.bootstrap.TriggerField = function(config){
13729     this.mimicing = false;
13730     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13731 };
13732
13733 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13734     /**
13735      * @cfg {String} triggerClass A CSS class to apply to the trigger
13736      */
13737      /**
13738      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13739      */
13740     hideTrigger:false,
13741
13742     /**
13743      * @cfg {Boolean} removable (true|false) special filter default false
13744      */
13745     removable : false,
13746     
13747     /** @cfg {Boolean} grow @hide */
13748     /** @cfg {Number} growMin @hide */
13749     /** @cfg {Number} growMax @hide */
13750
13751     /**
13752      * @hide 
13753      * @method
13754      */
13755     autoSize: Roo.emptyFn,
13756     // private
13757     monitorTab : true,
13758     // private
13759     deferHeight : true,
13760
13761     
13762     actionMode : 'wrap',
13763     
13764     caret : false,
13765     
13766     
13767     getAutoCreate : function(){
13768        
13769         var align = this.labelAlign || this.parentLabelAlign();
13770         
13771         var id = Roo.id();
13772         
13773         var cfg = {
13774             cls: 'form-group' //input-group
13775         };
13776         
13777         
13778         var input =  {
13779             tag: 'input',
13780             id : id,
13781             type : this.inputType,
13782             cls : 'form-control',
13783             autocomplete: 'new-password',
13784             placeholder : this.placeholder || '' 
13785             
13786         };
13787         if (this.name) {
13788             input.name = this.name;
13789         }
13790         if (this.size) {
13791             input.cls += ' input-' + this.size;
13792         }
13793         
13794         if (this.disabled) {
13795             input.disabled=true;
13796         }
13797         
13798         var inputblock = input;
13799         
13800         if(this.hasFeedback && !this.allowBlank){
13801             
13802             var feedback = {
13803                 tag: 'span',
13804                 cls: 'glyphicon form-control-feedback'
13805             };
13806             
13807             if(this.removable && !this.editable  ){
13808                 inputblock = {
13809                     cls : 'has-feedback',
13810                     cn :  [
13811                         inputblock,
13812                         {
13813                             tag: 'button',
13814                             html : 'x',
13815                             cls : 'roo-combo-removable-btn close'
13816                         },
13817                         feedback
13818                     ] 
13819                 };
13820             } else {
13821                 inputblock = {
13822                     cls : 'has-feedback',
13823                     cn :  [
13824                         inputblock,
13825                         feedback
13826                     ] 
13827                 };
13828             }
13829
13830         } else {
13831             if(this.removable && !this.editable ){
13832                 inputblock = {
13833                     cls : 'roo-removable',
13834                     cn :  [
13835                         inputblock,
13836                         {
13837                             tag: 'button',
13838                             html : 'x',
13839                             cls : 'roo-combo-removable-btn close'
13840                         }
13841                     ] 
13842                 };
13843             }
13844         }
13845         
13846         if (this.before || this.after) {
13847             
13848             inputblock = {
13849                 cls : 'input-group',
13850                 cn :  [] 
13851             };
13852             if (this.before) {
13853                 inputblock.cn.push({
13854                     tag :'span',
13855                     cls : 'input-group-addon input-group-prepend input-group-text',
13856                     html : this.before
13857                 });
13858             }
13859             
13860             inputblock.cn.push(input);
13861             
13862             if(this.hasFeedback && !this.allowBlank){
13863                 inputblock.cls += ' has-feedback';
13864                 inputblock.cn.push(feedback);
13865             }
13866             
13867             if (this.after) {
13868                 inputblock.cn.push({
13869                     tag :'span',
13870                     cls : 'input-group-addon input-group-append input-group-text',
13871                     html : this.after
13872                 });
13873             }
13874             
13875         };
13876         
13877       
13878         
13879         var ibwrap = inputblock;
13880         
13881         if(this.multiple){
13882             ibwrap = {
13883                 tag: 'ul',
13884                 cls: 'roo-select2-choices',
13885                 cn:[
13886                     {
13887                         tag: 'li',
13888                         cls: 'roo-select2-search-field',
13889                         cn: [
13890
13891                             inputblock
13892                         ]
13893                     }
13894                 ]
13895             };
13896                 
13897         }
13898         
13899         var combobox = {
13900             cls: 'roo-select2-container input-group',
13901             cn: [
13902                  {
13903                     tag: 'input',
13904                     type : 'hidden',
13905                     cls: 'form-hidden-field'
13906                 },
13907                 ibwrap
13908             ]
13909         };
13910         
13911         if(!this.multiple && this.showToggleBtn){
13912             
13913             var caret = {
13914                         tag: 'span',
13915                         cls: 'caret'
13916              };
13917             if (this.caret != false) {
13918                 caret = {
13919                      tag: 'i',
13920                      cls: 'fa fa-' + this.caret
13921                 };
13922                 
13923             }
13924             
13925             combobox.cn.push({
13926                 tag :'span',
13927                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13928                 cn : [
13929                     Roo.bootstrap.version == 3 ? caret : '',
13930                     {
13931                         tag: 'span',
13932                         cls: 'combobox-clear',
13933                         cn  : [
13934                             {
13935                                 tag : 'i',
13936                                 cls: 'icon-remove'
13937                             }
13938                         ]
13939                     }
13940                 ]
13941
13942             })
13943         }
13944         
13945         if(this.multiple){
13946             combobox.cls += ' roo-select2-container-multi';
13947         }
13948          var indicator = {
13949             tag : 'i',
13950             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13951             tooltip : 'This field is required'
13952         };
13953         if (Roo.bootstrap.version == 4) {
13954             indicator = {
13955                 tag : 'i',
13956                 style : 'display:none'
13957             };
13958         }
13959         
13960         
13961         if (align ==='left' && this.fieldLabel.length) {
13962             
13963             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13964
13965             cfg.cn = [
13966                 indicator,
13967                 {
13968                     tag: 'label',
13969                     'for' :  id,
13970                     cls : 'control-label',
13971                     html : this.fieldLabel
13972
13973                 },
13974                 {
13975                     cls : "", 
13976                     cn: [
13977                         combobox
13978                     ]
13979                 }
13980
13981             ];
13982             
13983             var labelCfg = cfg.cn[1];
13984             var contentCfg = cfg.cn[2];
13985             
13986             if(this.indicatorpos == 'right'){
13987                 cfg.cn = [
13988                     {
13989                         tag: 'label',
13990                         'for' :  id,
13991                         cls : 'control-label',
13992                         cn : [
13993                             {
13994                                 tag : 'span',
13995                                 html : this.fieldLabel
13996                             },
13997                             indicator
13998                         ]
13999                     },
14000                     {
14001                         cls : "", 
14002                         cn: [
14003                             combobox
14004                         ]
14005                     }
14006
14007                 ];
14008                 
14009                 labelCfg = cfg.cn[0];
14010                 contentCfg = cfg.cn[1];
14011             }
14012             
14013             if(this.labelWidth > 12){
14014                 labelCfg.style = "width: " + this.labelWidth + 'px';
14015             }
14016             
14017             if(this.labelWidth < 13 && this.labelmd == 0){
14018                 this.labelmd = this.labelWidth;
14019             }
14020             
14021             if(this.labellg > 0){
14022                 labelCfg.cls += ' col-lg-' + this.labellg;
14023                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14024             }
14025             
14026             if(this.labelmd > 0){
14027                 labelCfg.cls += ' col-md-' + this.labelmd;
14028                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14029             }
14030             
14031             if(this.labelsm > 0){
14032                 labelCfg.cls += ' col-sm-' + this.labelsm;
14033                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14034             }
14035             
14036             if(this.labelxs > 0){
14037                 labelCfg.cls += ' col-xs-' + this.labelxs;
14038                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14039             }
14040             
14041         } else if ( this.fieldLabel.length) {
14042 //                Roo.log(" label");
14043             cfg.cn = [
14044                 indicator,
14045                {
14046                    tag: 'label',
14047                    //cls : 'input-group-addon',
14048                    html : this.fieldLabel
14049
14050                },
14051
14052                combobox
14053
14054             ];
14055             
14056             if(this.indicatorpos == 'right'){
14057                 
14058                 cfg.cn = [
14059                     {
14060                        tag: 'label',
14061                        cn : [
14062                            {
14063                                tag : 'span',
14064                                html : this.fieldLabel
14065                            },
14066                            indicator
14067                        ]
14068
14069                     },
14070                     combobox
14071
14072                 ];
14073
14074             }
14075
14076         } else {
14077             
14078 //                Roo.log(" no label && no align");
14079                 cfg = combobox
14080                      
14081                 
14082         }
14083         
14084         var settings=this;
14085         ['xs','sm','md','lg'].map(function(size){
14086             if (settings[size]) {
14087                 cfg.cls += ' col-' + size + '-' + settings[size];
14088             }
14089         });
14090         
14091         return cfg;
14092         
14093     },
14094     
14095     
14096     
14097     // private
14098     onResize : function(w, h){
14099 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
14100 //        if(typeof w == 'number'){
14101 //            var x = w - this.trigger.getWidth();
14102 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14103 //            this.trigger.setStyle('left', x+'px');
14104 //        }
14105     },
14106
14107     // private
14108     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14109
14110     // private
14111     getResizeEl : function(){
14112         return this.inputEl();
14113     },
14114
14115     // private
14116     getPositionEl : function(){
14117         return this.inputEl();
14118     },
14119
14120     // private
14121     alignErrorIcon : function(){
14122         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14123     },
14124
14125     // private
14126     initEvents : function(){
14127         
14128         this.createList();
14129         
14130         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
14131         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14132         if(!this.multiple && this.showToggleBtn){
14133             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14134             if(this.hideTrigger){
14135                 this.trigger.setDisplayed(false);
14136             }
14137             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14138         }
14139         
14140         if(this.multiple){
14141             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14142         }
14143         
14144         if(this.removable && !this.editable && !this.tickable){
14145             var close = this.closeTriggerEl();
14146             
14147             if(close){
14148                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14149                 close.on('click', this.removeBtnClick, this, close);
14150             }
14151         }
14152         
14153         //this.trigger.addClassOnOver('x-form-trigger-over');
14154         //this.trigger.addClassOnClick('x-form-trigger-click');
14155         
14156         //if(!this.width){
14157         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14158         //}
14159     },
14160     
14161     closeTriggerEl : function()
14162     {
14163         var close = this.el.select('.roo-combo-removable-btn', true).first();
14164         return close ? close : false;
14165     },
14166     
14167     removeBtnClick : function(e, h, el)
14168     {
14169         e.preventDefault();
14170         
14171         if(this.fireEvent("remove", this) !== false){
14172             this.reset();
14173             this.fireEvent("afterremove", this)
14174         }
14175     },
14176     
14177     createList : function()
14178     {
14179         this.list = Roo.get(document.body).createChild({
14180             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14181             cls: 'typeahead typeahead-long dropdown-menu shadow',
14182             style: 'display:none'
14183         });
14184         
14185         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14186         
14187     },
14188
14189     // private
14190     initTrigger : function(){
14191        
14192     },
14193
14194     // private
14195     onDestroy : function(){
14196         if(this.trigger){
14197             this.trigger.removeAllListeners();
14198           //  this.trigger.remove();
14199         }
14200         //if(this.wrap){
14201         //    this.wrap.remove();
14202         //}
14203         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
14204     },
14205
14206     // private
14207     onFocus : function(){
14208         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
14209         /*
14210         if(!this.mimicing){
14211             this.wrap.addClass('x-trigger-wrap-focus');
14212             this.mimicing = true;
14213             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14214             if(this.monitorTab){
14215                 this.el.on("keydown", this.checkTab, this);
14216             }
14217         }
14218         */
14219     },
14220
14221     // private
14222     checkTab : function(e){
14223         if(e.getKey() == e.TAB){
14224             this.triggerBlur();
14225         }
14226     },
14227
14228     // private
14229     onBlur : function(){
14230         // do nothing
14231     },
14232
14233     // private
14234     mimicBlur : function(e, t){
14235         /*
14236         if(!this.wrap.contains(t) && this.validateBlur()){
14237             this.triggerBlur();
14238         }
14239         */
14240     },
14241
14242     // private
14243     triggerBlur : function(){
14244         this.mimicing = false;
14245         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14246         if(this.monitorTab){
14247             this.el.un("keydown", this.checkTab, this);
14248         }
14249         //this.wrap.removeClass('x-trigger-wrap-focus');
14250         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
14251     },
14252
14253     // private
14254     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14255     validateBlur : function(e, t){
14256         return true;
14257     },
14258
14259     // private
14260     onDisable : function(){
14261         this.inputEl().dom.disabled = true;
14262         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
14263         //if(this.wrap){
14264         //    this.wrap.addClass('x-item-disabled');
14265         //}
14266     },
14267
14268     // private
14269     onEnable : function(){
14270         this.inputEl().dom.disabled = false;
14271         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
14272         //if(this.wrap){
14273         //    this.el.removeClass('x-item-disabled');
14274         //}
14275     },
14276
14277     // private
14278     onShow : function(){
14279         var ae = this.getActionEl();
14280         
14281         if(ae){
14282             ae.dom.style.display = '';
14283             ae.dom.style.visibility = 'visible';
14284         }
14285     },
14286
14287     // private
14288     
14289     onHide : function(){
14290         var ae = this.getActionEl();
14291         ae.dom.style.display = 'none';
14292     },
14293
14294     /**
14295      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14296      * by an implementing function.
14297      * @method
14298      * @param {EventObject} e
14299      */
14300     onTriggerClick : Roo.emptyFn
14301 });
14302  
14303 /*
14304 * Licence: LGPL
14305 */
14306
14307 /**
14308  * @class Roo.bootstrap.CardUploader
14309  * @extends Roo.bootstrap.Button
14310  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14311  * @cfg {Number} errorTimeout default 3000
14312  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14313  * @cfg {Array}  html The button text.
14314
14315  *
14316  * @constructor
14317  * Create a new CardUploader
14318  * @param {Object} config The config object
14319  */
14320
14321 Roo.bootstrap.CardUploader = function(config){
14322     
14323  
14324     
14325     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
14326     
14327     
14328     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14329         return r.data.id
14330      });
14331     
14332      this.addEvents({
14333          // raw events
14334         /**
14335          * @event preview
14336          * When a image is clicked on - and needs to display a slideshow or similar..
14337          * @param {Roo.bootstrap.Card} this
14338          * @param {Object} The image information data 
14339          *
14340          */
14341         'preview' : true,
14342          /**
14343          * @event download
14344          * When a the download link is clicked
14345          * @param {Roo.bootstrap.Card} this
14346          * @param {Object} The image information data  contains 
14347          */
14348         'download' : true
14349         
14350     });
14351 };
14352  
14353 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
14354     
14355      
14356     errorTimeout : 3000,
14357      
14358     images : false,
14359    
14360     fileCollection : false,
14361     allowBlank : true,
14362     
14363     getAutoCreate : function()
14364     {
14365         
14366         var cfg =  {
14367             cls :'form-group' ,
14368             cn : [
14369                
14370                 {
14371                     tag: 'label',
14372                    //cls : 'input-group-addon',
14373                     html : this.fieldLabel
14374
14375                 },
14376
14377                 {
14378                     tag: 'input',
14379                     type : 'hidden',
14380                     name : this.name,
14381                     value : this.value,
14382                     cls : 'd-none  form-control'
14383                 },
14384                 
14385                 {
14386                     tag: 'input',
14387                     multiple : 'multiple',
14388                     type : 'file',
14389                     cls : 'd-none  roo-card-upload-selector'
14390                 },
14391                 
14392                 {
14393                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14394                 },
14395                 {
14396                     cls : 'card-columns roo-card-uploader-container'
14397                 }
14398
14399             ]
14400         };
14401            
14402          
14403         return cfg;
14404     },
14405     
14406     getChildContainer : function() /// what children are added to.
14407     {
14408         return this.containerEl;
14409     },
14410    
14411     getButtonContainer : function() /// what children are added to.
14412     {
14413         return this.el.select(".roo-card-uploader-button-container").first();
14414     },
14415    
14416     initEvents : function()
14417     {
14418         
14419         Roo.bootstrap.Input.prototype.initEvents.call(this);
14420         
14421         var t = this;
14422         this.addxtype({
14423             xns: Roo.bootstrap,
14424
14425             xtype : 'Button',
14426             container_method : 'getButtonContainer' ,            
14427             html :  this.html, // fix changable?
14428             cls : 'w-100 ',
14429             listeners : {
14430                 'click' : function(btn, e) {
14431                     t.onClick(e);
14432                 }
14433             }
14434         });
14435         
14436         
14437         
14438         
14439         this.urlAPI = (window.createObjectURL && window) || 
14440                                 (window.URL && URL.revokeObjectURL && URL) || 
14441                                 (window.webkitURL && webkitURL);
14442                         
14443          
14444          
14445          
14446         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14447         
14448         this.selectorEl.on('change', this.onFileSelected, this);
14449         if (this.images) {
14450             var t = this;
14451             this.images.forEach(function(img) {
14452                 t.addCard(img)
14453             });
14454             this.images = false;
14455         }
14456         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14457          
14458        
14459     },
14460     
14461    
14462     onClick : function(e)
14463     {
14464         e.preventDefault();
14465          
14466         this.selectorEl.dom.click();
14467          
14468     },
14469     
14470     onFileSelected : function(e)
14471     {
14472         e.preventDefault();
14473         
14474         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14475             return;
14476         }
14477         
14478         Roo.each(this.selectorEl.dom.files, function(file){    
14479             this.addFile(file);
14480         }, this);
14481          
14482     },
14483     
14484       
14485     
14486       
14487     
14488     addFile : function(file)
14489     {
14490            
14491         if(typeof(file) === 'string'){
14492             throw "Add file by name?"; // should not happen
14493             return;
14494         }
14495         
14496         if(!file || !this.urlAPI){
14497             return;
14498         }
14499         
14500         // file;
14501         // file.type;
14502         
14503         var _this = this;
14504         
14505         
14506         var url = _this.urlAPI.createObjectURL( file);
14507            
14508         this.addCard({
14509             id : Roo.bootstrap.CardUploader.ID--,
14510             is_uploaded : false,
14511             src : url,
14512             srcfile : file,
14513             title : file.name,
14514             mimetype : file.type,
14515             preview : false,
14516             is_deleted : 0
14517         });
14518         
14519     },
14520     
14521     /**
14522      * addCard - add an Attachment to the uploader
14523      * @param data - the data about the image to upload
14524      *
14525      * {
14526           id : 123
14527           title : "Title of file",
14528           is_uploaded : false,
14529           src : "http://.....",
14530           srcfile : { the File upload object },
14531           mimetype : file.type,
14532           preview : false,
14533           is_deleted : 0
14534           .. any other data...
14535         }
14536      *
14537      * 
14538     */
14539     
14540     addCard : function (data)
14541     {
14542         // hidden input element?
14543         // if the file is not an image...
14544         //then we need to use something other that and header_image
14545         var t = this;
14546         //   remove.....
14547         var footer = [
14548             {
14549                 xns : Roo.bootstrap,
14550                 xtype : 'CardFooter',
14551                  items: [
14552                     {
14553                         xns : Roo.bootstrap,
14554                         xtype : 'Element',
14555                         cls : 'd-flex',
14556                         items : [
14557                             
14558                             {
14559                                 xns : Roo.bootstrap,
14560                                 xtype : 'Button',
14561                                 html : String.format("<small>{0}</small>", data.title),
14562                                 cls : 'col-10 text-left',
14563                                 size: 'sm',
14564                                 weight: 'link',
14565                                 fa : 'download',
14566                                 listeners : {
14567                                     click : function() {
14568                                      
14569                                         t.fireEvent( "download", t, data );
14570                                     }
14571                                 }
14572                             },
14573                           
14574                             {
14575                                 xns : Roo.bootstrap,
14576                                 xtype : 'Button',
14577                                 style: 'max-height: 28px; ',
14578                                 size : 'sm',
14579                                 weight: 'danger',
14580                                 cls : 'col-2',
14581                                 fa : 'times',
14582                                 listeners : {
14583                                     click : function() {
14584                                         t.removeCard(data.id)
14585                                     }
14586                                 }
14587                             }
14588                         ]
14589                     }
14590                     
14591                 ] 
14592             }
14593             
14594         ];
14595         
14596         var cn = this.addxtype(
14597             {
14598                  
14599                 xns : Roo.bootstrap,
14600                 xtype : 'Card',
14601                 closeable : true,
14602                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14603                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14604                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14605                 data : data,
14606                 html : false,
14607                  
14608                 items : footer,
14609                 initEvents : function() {
14610                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14611                     var card = this;
14612                     this.imgEl = this.el.select('.card-img-top').first();
14613                     if (this.imgEl) {
14614                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14615                         this.imgEl.set({ 'pointer' : 'cursor' });
14616                                   
14617                     }
14618                     this.getCardFooter().addClass('p-1');
14619                     
14620                   
14621                 }
14622                 
14623             }
14624         );
14625         // dont' really need ot update items.
14626         // this.items.push(cn);
14627         this.fileCollection.add(cn);
14628         
14629         if (!data.srcfile) {
14630             this.updateInput();
14631             return;
14632         }
14633             
14634         var _t = this;
14635         var reader = new FileReader();
14636         reader.addEventListener("load", function() {  
14637             data.srcdata =  reader.result;
14638             _t.updateInput();
14639         });
14640         reader.readAsDataURL(data.srcfile);
14641         
14642         
14643         
14644     },
14645     removeCard : function(id)
14646     {
14647         
14648         var card  = this.fileCollection.get(id);
14649         card.data.is_deleted = 1;
14650         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14651         //this.fileCollection.remove(card);
14652         //this.items = this.items.filter(function(e) { return e != card });
14653         // dont' really need ot update items.
14654         card.el.dom.parentNode.removeChild(card.el.dom);
14655         this.updateInput();
14656
14657         
14658     },
14659     reset: function()
14660     {
14661         this.fileCollection.each(function(card) {
14662             if (card.el.dom && card.el.dom.parentNode) {
14663                 card.el.dom.parentNode.removeChild(card.el.dom);
14664             }
14665         });
14666         this.fileCollection.clear();
14667         this.updateInput();
14668     },
14669     
14670     updateInput : function()
14671     {
14672          var data = [];
14673         this.fileCollection.each(function(e) {
14674             data.push(e.data);
14675             
14676         });
14677         this.inputEl().dom.value = JSON.stringify(data);
14678         
14679         
14680         
14681     }
14682     
14683     
14684 });
14685
14686
14687 Roo.bootstrap.CardUploader.ID = -1;/*
14688  * Based on:
14689  * Ext JS Library 1.1.1
14690  * Copyright(c) 2006-2007, Ext JS, LLC.
14691  *
14692  * Originally Released Under LGPL - original licence link has changed is not relivant.
14693  *
14694  * Fork - LGPL
14695  * <script type="text/javascript">
14696  */
14697
14698
14699 /**
14700  * @class Roo.data.SortTypes
14701  * @singleton
14702  * Defines the default sorting (casting?) comparison functions used when sorting data.
14703  */
14704 Roo.data.SortTypes = {
14705     /**
14706      * Default sort that does nothing
14707      * @param {Mixed} s The value being converted
14708      * @return {Mixed} The comparison value
14709      */
14710     none : function(s){
14711         return s;
14712     },
14713     
14714     /**
14715      * The regular expression used to strip tags
14716      * @type {RegExp}
14717      * @property
14718      */
14719     stripTagsRE : /<\/?[^>]+>/gi,
14720     
14721     /**
14722      * Strips all HTML tags to sort on text only
14723      * @param {Mixed} s The value being converted
14724      * @return {String} The comparison value
14725      */
14726     asText : function(s){
14727         return String(s).replace(this.stripTagsRE, "");
14728     },
14729     
14730     /**
14731      * Strips all HTML tags to sort on text only - Case insensitive
14732      * @param {Mixed} s The value being converted
14733      * @return {String} The comparison value
14734      */
14735     asUCText : function(s){
14736         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14737     },
14738     
14739     /**
14740      * Case insensitive string
14741      * @param {Mixed} s The value being converted
14742      * @return {String} The comparison value
14743      */
14744     asUCString : function(s) {
14745         return String(s).toUpperCase();
14746     },
14747     
14748     /**
14749      * Date sorting
14750      * @param {Mixed} s The value being converted
14751      * @return {Number} The comparison value
14752      */
14753     asDate : function(s) {
14754         if(!s){
14755             return 0;
14756         }
14757         if(s instanceof Date){
14758             return s.getTime();
14759         }
14760         return Date.parse(String(s));
14761     },
14762     
14763     /**
14764      * Float sorting
14765      * @param {Mixed} s The value being converted
14766      * @return {Float} The comparison value
14767      */
14768     asFloat : function(s) {
14769         var val = parseFloat(String(s).replace(/,/g, ""));
14770         if(isNaN(val)) {
14771             val = 0;
14772         }
14773         return val;
14774     },
14775     
14776     /**
14777      * Integer sorting
14778      * @param {Mixed} s The value being converted
14779      * @return {Number} The comparison value
14780      */
14781     asInt : function(s) {
14782         var val = parseInt(String(s).replace(/,/g, ""));
14783         if(isNaN(val)) {
14784             val = 0;
14785         }
14786         return val;
14787     }
14788 };/*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798
14799 /**
14800 * @class Roo.data.Record
14801  * Instances of this class encapsulate both record <em>definition</em> information, and record
14802  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14803  * to access Records cached in an {@link Roo.data.Store} object.<br>
14804  * <p>
14805  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14806  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14807  * objects.<br>
14808  * <p>
14809  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14810  * @constructor
14811  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14812  * {@link #create}. The parameters are the same.
14813  * @param {Array} data An associative Array of data values keyed by the field name.
14814  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14815  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14816  * not specified an integer id is generated.
14817  */
14818 Roo.data.Record = function(data, id){
14819     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14820     this.data = data;
14821 };
14822
14823 /**
14824  * Generate a constructor for a specific record layout.
14825  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14826  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14827  * Each field definition object may contain the following properties: <ul>
14828  * <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,
14829  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14830  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14831  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14832  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14833  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14834  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14835  * this may be omitted.</p></li>
14836  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14837  * <ul><li>auto (Default, implies no conversion)</li>
14838  * <li>string</li>
14839  * <li>int</li>
14840  * <li>float</li>
14841  * <li>boolean</li>
14842  * <li>date</li></ul></p></li>
14843  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14844  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14845  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14846  * by the Reader into an object that will be stored in the Record. It is passed the
14847  * following parameters:<ul>
14848  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14849  * </ul></p></li>
14850  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14851  * </ul>
14852  * <br>usage:<br><pre><code>
14853 var TopicRecord = Roo.data.Record.create(
14854     {name: 'title', mapping: 'topic_title'},
14855     {name: 'author', mapping: 'username'},
14856     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14857     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14858     {name: 'lastPoster', mapping: 'user2'},
14859     {name: 'excerpt', mapping: 'post_text'}
14860 );
14861
14862 var myNewRecord = new TopicRecord({
14863     title: 'Do my job please',
14864     author: 'noobie',
14865     totalPosts: 1,
14866     lastPost: new Date(),
14867     lastPoster: 'Animal',
14868     excerpt: 'No way dude!'
14869 });
14870 myStore.add(myNewRecord);
14871 </code></pre>
14872  * @method create
14873  * @static
14874  */
14875 Roo.data.Record.create = function(o){
14876     var f = function(){
14877         f.superclass.constructor.apply(this, arguments);
14878     };
14879     Roo.extend(f, Roo.data.Record);
14880     var p = f.prototype;
14881     p.fields = new Roo.util.MixedCollection(false, function(field){
14882         return field.name;
14883     });
14884     for(var i = 0, len = o.length; i < len; i++){
14885         p.fields.add(new Roo.data.Field(o[i]));
14886     }
14887     f.getField = function(name){
14888         return p.fields.get(name);  
14889     };
14890     return f;
14891 };
14892
14893 Roo.data.Record.AUTO_ID = 1000;
14894 Roo.data.Record.EDIT = 'edit';
14895 Roo.data.Record.REJECT = 'reject';
14896 Roo.data.Record.COMMIT = 'commit';
14897
14898 Roo.data.Record.prototype = {
14899     /**
14900      * Readonly flag - true if this record has been modified.
14901      * @type Boolean
14902      */
14903     dirty : false,
14904     editing : false,
14905     error: null,
14906     modified: null,
14907
14908     // private
14909     join : function(store){
14910         this.store = store;
14911     },
14912
14913     /**
14914      * Set the named field to the specified value.
14915      * @param {String} name The name of the field to set.
14916      * @param {Object} value The value to set the field to.
14917      */
14918     set : function(name, value){
14919         if(this.data[name] == value){
14920             return;
14921         }
14922         this.dirty = true;
14923         if(!this.modified){
14924             this.modified = {};
14925         }
14926         if(typeof this.modified[name] == 'undefined'){
14927             this.modified[name] = this.data[name];
14928         }
14929         this.data[name] = value;
14930         if(!this.editing && this.store){
14931             this.store.afterEdit(this);
14932         }       
14933     },
14934
14935     /**
14936      * Get the value of the named field.
14937      * @param {String} name The name of the field to get the value of.
14938      * @return {Object} The value of the field.
14939      */
14940     get : function(name){
14941         return this.data[name]; 
14942     },
14943
14944     // private
14945     beginEdit : function(){
14946         this.editing = true;
14947         this.modified = {}; 
14948     },
14949
14950     // private
14951     cancelEdit : function(){
14952         this.editing = false;
14953         delete this.modified;
14954     },
14955
14956     // private
14957     endEdit : function(){
14958         this.editing = false;
14959         if(this.dirty && this.store){
14960             this.store.afterEdit(this);
14961         }
14962     },
14963
14964     /**
14965      * Usually called by the {@link Roo.data.Store} which owns the Record.
14966      * Rejects all changes made to the Record since either creation, or the last commit operation.
14967      * Modified fields are reverted to their original values.
14968      * <p>
14969      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14970      * of reject operations.
14971      */
14972     reject : function(){
14973         var m = this.modified;
14974         for(var n in m){
14975             if(typeof m[n] != "function"){
14976                 this.data[n] = m[n];
14977             }
14978         }
14979         this.dirty = false;
14980         delete this.modified;
14981         this.editing = false;
14982         if(this.store){
14983             this.store.afterReject(this);
14984         }
14985     },
14986
14987     /**
14988      * Usually called by the {@link Roo.data.Store} which owns the Record.
14989      * Commits all changes made to the Record since either creation, or the last commit operation.
14990      * <p>
14991      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14992      * of commit operations.
14993      */
14994     commit : function(){
14995         this.dirty = false;
14996         delete this.modified;
14997         this.editing = false;
14998         if(this.store){
14999             this.store.afterCommit(this);
15000         }
15001     },
15002
15003     // private
15004     hasError : function(){
15005         return this.error != null;
15006     },
15007
15008     // private
15009     clearError : function(){
15010         this.error = null;
15011     },
15012
15013     /**
15014      * Creates a copy of this record.
15015      * @param {String} id (optional) A new record id if you don't want to use this record's id
15016      * @return {Record}
15017      */
15018     copy : function(newId) {
15019         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15020     }
15021 };/*
15022  * Based on:
15023  * Ext JS Library 1.1.1
15024  * Copyright(c) 2006-2007, Ext JS, LLC.
15025  *
15026  * Originally Released Under LGPL - original licence link has changed is not relivant.
15027  *
15028  * Fork - LGPL
15029  * <script type="text/javascript">
15030  */
15031
15032
15033
15034 /**
15035  * @class Roo.data.Store
15036  * @extends Roo.util.Observable
15037  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15038  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15039  * <p>
15040  * 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
15041  * has no knowledge of the format of the data returned by the Proxy.<br>
15042  * <p>
15043  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15044  * instances from the data object. These records are cached and made available through accessor functions.
15045  * @constructor
15046  * Creates a new Store.
15047  * @param {Object} config A config object containing the objects needed for the Store to access data,
15048  * and read the data into Records.
15049  */
15050 Roo.data.Store = function(config){
15051     this.data = new Roo.util.MixedCollection(false);
15052     this.data.getKey = function(o){
15053         return o.id;
15054     };
15055     this.baseParams = {};
15056     // private
15057     this.paramNames = {
15058         "start" : "start",
15059         "limit" : "limit",
15060         "sort" : "sort",
15061         "dir" : "dir",
15062         "multisort" : "_multisort"
15063     };
15064
15065     if(config && config.data){
15066         this.inlineData = config.data;
15067         delete config.data;
15068     }
15069
15070     Roo.apply(this, config);
15071     
15072     if(this.reader){ // reader passed
15073         this.reader = Roo.factory(this.reader, Roo.data);
15074         this.reader.xmodule = this.xmodule || false;
15075         if(!this.recordType){
15076             this.recordType = this.reader.recordType;
15077         }
15078         if(this.reader.onMetaChange){
15079             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15080         }
15081     }
15082
15083     if(this.recordType){
15084         this.fields = this.recordType.prototype.fields;
15085     }
15086     this.modified = [];
15087
15088     this.addEvents({
15089         /**
15090          * @event datachanged
15091          * Fires when the data cache has changed, and a widget which is using this Store
15092          * as a Record cache should refresh its view.
15093          * @param {Store} this
15094          */
15095         datachanged : true,
15096         /**
15097          * @event metachange
15098          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15099          * @param {Store} this
15100          * @param {Object} meta The JSON metadata
15101          */
15102         metachange : true,
15103         /**
15104          * @event add
15105          * Fires when Records have been added to the Store
15106          * @param {Store} this
15107          * @param {Roo.data.Record[]} records The array of Records added
15108          * @param {Number} index The index at which the record(s) were added
15109          */
15110         add : true,
15111         /**
15112          * @event remove
15113          * Fires when a Record has been removed from the Store
15114          * @param {Store} this
15115          * @param {Roo.data.Record} record The Record that was removed
15116          * @param {Number} index The index at which the record was removed
15117          */
15118         remove : true,
15119         /**
15120          * @event update
15121          * Fires when a Record has been updated
15122          * @param {Store} this
15123          * @param {Roo.data.Record} record The Record that was updated
15124          * @param {String} operation The update operation being performed.  Value may be one of:
15125          * <pre><code>
15126  Roo.data.Record.EDIT
15127  Roo.data.Record.REJECT
15128  Roo.data.Record.COMMIT
15129          * </code></pre>
15130          */
15131         update : true,
15132         /**
15133          * @event clear
15134          * Fires when the data cache has been cleared.
15135          * @param {Store} this
15136          */
15137         clear : true,
15138         /**
15139          * @event beforeload
15140          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15141          * the load action will be canceled.
15142          * @param {Store} this
15143          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15144          */
15145         beforeload : true,
15146         /**
15147          * @event beforeloadadd
15148          * Fires after a new set of Records has been loaded.
15149          * @param {Store} this
15150          * @param {Roo.data.Record[]} records The Records that were loaded
15151          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15152          */
15153         beforeloadadd : true,
15154         /**
15155          * @event load
15156          * Fires after a new set of Records has been loaded, before they are added to the store.
15157          * @param {Store} this
15158          * @param {Roo.data.Record[]} records The Records that were loaded
15159          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15160          * @params {Object} return from reader
15161          */
15162         load : true,
15163         /**
15164          * @event loadexception
15165          * Fires if an exception occurs in the Proxy during loading.
15166          * Called with the signature of the Proxy's "loadexception" event.
15167          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15168          * 
15169          * @param {Proxy} 
15170          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15171          * @param {Object} load options 
15172          * @param {Object} jsonData from your request (normally this contains the Exception)
15173          */
15174         loadexception : true
15175     });
15176     
15177     if(this.proxy){
15178         this.proxy = Roo.factory(this.proxy, Roo.data);
15179         this.proxy.xmodule = this.xmodule || false;
15180         this.relayEvents(this.proxy,  ["loadexception"]);
15181     }
15182     this.sortToggle = {};
15183     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15184
15185     Roo.data.Store.superclass.constructor.call(this);
15186
15187     if(this.inlineData){
15188         this.loadData(this.inlineData);
15189         delete this.inlineData;
15190     }
15191 };
15192
15193 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15194      /**
15195     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15196     * without a remote query - used by combo/forms at present.
15197     */
15198     
15199     /**
15200     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15201     */
15202     /**
15203     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15204     */
15205     /**
15206     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15207     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15208     */
15209     /**
15210     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15211     * on any HTTP request
15212     */
15213     /**
15214     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15215     */
15216     /**
15217     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15218     */
15219     multiSort: false,
15220     /**
15221     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15222     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15223     */
15224     remoteSort : false,
15225
15226     /**
15227     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15228      * loaded or when a record is removed. (defaults to false).
15229     */
15230     pruneModifiedRecords : false,
15231
15232     // private
15233     lastOptions : null,
15234
15235     /**
15236      * Add Records to the Store and fires the add event.
15237      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15238      */
15239     add : function(records){
15240         records = [].concat(records);
15241         for(var i = 0, len = records.length; i < len; i++){
15242             records[i].join(this);
15243         }
15244         var index = this.data.length;
15245         this.data.addAll(records);
15246         this.fireEvent("add", this, records, index);
15247     },
15248
15249     /**
15250      * Remove a Record from the Store and fires the remove event.
15251      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15252      */
15253     remove : function(record){
15254         var index = this.data.indexOf(record);
15255         this.data.removeAt(index);
15256  
15257         if(this.pruneModifiedRecords){
15258             this.modified.remove(record);
15259         }
15260         this.fireEvent("remove", this, record, index);
15261     },
15262
15263     /**
15264      * Remove all Records from the Store and fires the clear event.
15265      */
15266     removeAll : function(){
15267         this.data.clear();
15268         if(this.pruneModifiedRecords){
15269             this.modified = [];
15270         }
15271         this.fireEvent("clear", this);
15272     },
15273
15274     /**
15275      * Inserts Records to the Store at the given index and fires the add event.
15276      * @param {Number} index The start index at which to insert the passed Records.
15277      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15278      */
15279     insert : function(index, records){
15280         records = [].concat(records);
15281         for(var i = 0, len = records.length; i < len; i++){
15282             this.data.insert(index, records[i]);
15283             records[i].join(this);
15284         }
15285         this.fireEvent("add", this, records, index);
15286     },
15287
15288     /**
15289      * Get the index within the cache of the passed Record.
15290      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15291      * @return {Number} The index of the passed Record. Returns -1 if not found.
15292      */
15293     indexOf : function(record){
15294         return this.data.indexOf(record);
15295     },
15296
15297     /**
15298      * Get the index within the cache of the Record with the passed id.
15299      * @param {String} id The id of the Record to find.
15300      * @return {Number} The index of the Record. Returns -1 if not found.
15301      */
15302     indexOfId : function(id){
15303         return this.data.indexOfKey(id);
15304     },
15305
15306     /**
15307      * Get the Record with the specified id.
15308      * @param {String} id The id of the Record to find.
15309      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15310      */
15311     getById : function(id){
15312         return this.data.key(id);
15313     },
15314
15315     /**
15316      * Get the Record at the specified index.
15317      * @param {Number} index The index of the Record to find.
15318      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15319      */
15320     getAt : function(index){
15321         return this.data.itemAt(index);
15322     },
15323
15324     /**
15325      * Returns a range of Records between specified indices.
15326      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15327      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15328      * @return {Roo.data.Record[]} An array of Records
15329      */
15330     getRange : function(start, end){
15331         return this.data.getRange(start, end);
15332     },
15333
15334     // private
15335     storeOptions : function(o){
15336         o = Roo.apply({}, o);
15337         delete o.callback;
15338         delete o.scope;
15339         this.lastOptions = o;
15340     },
15341
15342     /**
15343      * Loads the Record cache from the configured Proxy using the configured Reader.
15344      * <p>
15345      * If using remote paging, then the first load call must specify the <em>start</em>
15346      * and <em>limit</em> properties in the options.params property to establish the initial
15347      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15348      * <p>
15349      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15350      * and this call will return before the new data has been loaded. Perform any post-processing
15351      * in a callback function, or in a "load" event handler.</strong>
15352      * <p>
15353      * @param {Object} options An object containing properties which control loading options:<ul>
15354      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15355      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15356      * passed the following arguments:<ul>
15357      * <li>r : Roo.data.Record[]</li>
15358      * <li>options: Options object from the load call</li>
15359      * <li>success: Boolean success indicator</li></ul></li>
15360      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15361      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15362      * </ul>
15363      */
15364     load : function(options){
15365         options = options || {};
15366         if(this.fireEvent("beforeload", this, options) !== false){
15367             this.storeOptions(options);
15368             var p = Roo.apply(options.params || {}, this.baseParams);
15369             // if meta was not loaded from remote source.. try requesting it.
15370             if (!this.reader.metaFromRemote) {
15371                 p._requestMeta = 1;
15372             }
15373             if(this.sortInfo && this.remoteSort){
15374                 var pn = this.paramNames;
15375                 p[pn["sort"]] = this.sortInfo.field;
15376                 p[pn["dir"]] = this.sortInfo.direction;
15377             }
15378             if (this.multiSort) {
15379                 var pn = this.paramNames;
15380                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15381             }
15382             
15383             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15384         }
15385     },
15386
15387     /**
15388      * Reloads the Record cache from the configured Proxy using the configured Reader and
15389      * the options from the last load operation performed.
15390      * @param {Object} options (optional) An object containing properties which may override the options
15391      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15392      * the most recently used options are reused).
15393      */
15394     reload : function(options){
15395         this.load(Roo.applyIf(options||{}, this.lastOptions));
15396     },
15397
15398     // private
15399     // Called as a callback by the Reader during a load operation.
15400     loadRecords : function(o, options, success){
15401         if(!o || success === false){
15402             if(success !== false){
15403                 this.fireEvent("load", this, [], options, o);
15404             }
15405             if(options.callback){
15406                 options.callback.call(options.scope || this, [], options, false);
15407             }
15408             return;
15409         }
15410         // if data returned failure - throw an exception.
15411         if (o.success === false) {
15412             // show a message if no listener is registered.
15413             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15414                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15415             }
15416             // loadmask wil be hooked into this..
15417             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15418             return;
15419         }
15420         var r = o.records, t = o.totalRecords || r.length;
15421         
15422         this.fireEvent("beforeloadadd", this, r, options, o);
15423         
15424         if(!options || options.add !== true){
15425             if(this.pruneModifiedRecords){
15426                 this.modified = [];
15427             }
15428             for(var i = 0, len = r.length; i < len; i++){
15429                 r[i].join(this);
15430             }
15431             if(this.snapshot){
15432                 this.data = this.snapshot;
15433                 delete this.snapshot;
15434             }
15435             this.data.clear();
15436             this.data.addAll(r);
15437             this.totalLength = t;
15438             this.applySort();
15439             this.fireEvent("datachanged", this);
15440         }else{
15441             this.totalLength = Math.max(t, this.data.length+r.length);
15442             this.add(r);
15443         }
15444         
15445         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15446                 
15447             var e = new Roo.data.Record({});
15448
15449             e.set(this.parent.displayField, this.parent.emptyTitle);
15450             e.set(this.parent.valueField, '');
15451
15452             this.insert(0, e);
15453         }
15454             
15455         this.fireEvent("load", this, r, options, o);
15456         if(options.callback){
15457             options.callback.call(options.scope || this, r, options, true);
15458         }
15459     },
15460
15461
15462     /**
15463      * Loads data from a passed data block. A Reader which understands the format of the data
15464      * must have been configured in the constructor.
15465      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15466      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15467      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15468      */
15469     loadData : function(o, append){
15470         var r = this.reader.readRecords(o);
15471         this.loadRecords(r, {add: append}, true);
15472     },
15473     
15474      /**
15475      * using 'cn' the nested child reader read the child array into it's child stores.
15476      * @param {Object} rec The record with a 'children array
15477      */
15478     loadDataFromChildren : function(rec)
15479     {
15480         this.loadData(this.reader.toLoadData(rec));
15481     },
15482     
15483
15484     /**
15485      * Gets the number of cached records.
15486      * <p>
15487      * <em>If using paging, this may not be the total size of the dataset. If the data object
15488      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15489      * the data set size</em>
15490      */
15491     getCount : function(){
15492         return this.data.length || 0;
15493     },
15494
15495     /**
15496      * Gets the total number of records in the dataset as returned by the server.
15497      * <p>
15498      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15499      * the dataset size</em>
15500      */
15501     getTotalCount : function(){
15502         return this.totalLength || 0;
15503     },
15504
15505     /**
15506      * Returns the sort state of the Store as an object with two properties:
15507      * <pre><code>
15508  field {String} The name of the field by which the Records are sorted
15509  direction {String} The sort order, "ASC" or "DESC"
15510      * </code></pre>
15511      */
15512     getSortState : function(){
15513         return this.sortInfo;
15514     },
15515
15516     // private
15517     applySort : function(){
15518         if(this.sortInfo && !this.remoteSort){
15519             var s = this.sortInfo, f = s.field;
15520             var st = this.fields.get(f).sortType;
15521             var fn = function(r1, r2){
15522                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15523                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15524             };
15525             this.data.sort(s.direction, fn);
15526             if(this.snapshot && this.snapshot != this.data){
15527                 this.snapshot.sort(s.direction, fn);
15528             }
15529         }
15530     },
15531
15532     /**
15533      * Sets the default sort column and order to be used by the next load operation.
15534      * @param {String} fieldName The name of the field to sort by.
15535      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15536      */
15537     setDefaultSort : function(field, dir){
15538         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15539     },
15540
15541     /**
15542      * Sort the Records.
15543      * If remote sorting is used, the sort is performed on the server, and the cache is
15544      * reloaded. If local sorting is used, the cache is sorted internally.
15545      * @param {String} fieldName The name of the field to sort by.
15546      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15547      */
15548     sort : function(fieldName, dir){
15549         var f = this.fields.get(fieldName);
15550         if(!dir){
15551             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15552             
15553             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15554                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15555             }else{
15556                 dir = f.sortDir;
15557             }
15558         }
15559         this.sortToggle[f.name] = dir;
15560         this.sortInfo = {field: f.name, direction: dir};
15561         if(!this.remoteSort){
15562             this.applySort();
15563             this.fireEvent("datachanged", this);
15564         }else{
15565             this.load(this.lastOptions);
15566         }
15567     },
15568
15569     /**
15570      * Calls the specified function for each of the Records in the cache.
15571      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15572      * Returning <em>false</em> aborts and exits the iteration.
15573      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15574      */
15575     each : function(fn, scope){
15576         this.data.each(fn, scope);
15577     },
15578
15579     /**
15580      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15581      * (e.g., during paging).
15582      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15583      */
15584     getModifiedRecords : function(){
15585         return this.modified;
15586     },
15587
15588     // private
15589     createFilterFn : function(property, value, anyMatch){
15590         if(!value.exec){ // not a regex
15591             value = String(value);
15592             if(value.length == 0){
15593                 return false;
15594             }
15595             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15596         }
15597         return function(r){
15598             return value.test(r.data[property]);
15599         };
15600     },
15601
15602     /**
15603      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15604      * @param {String} property A field on your records
15605      * @param {Number} start The record index to start at (defaults to 0)
15606      * @param {Number} end The last record index to include (defaults to length - 1)
15607      * @return {Number} The sum
15608      */
15609     sum : function(property, start, end){
15610         var rs = this.data.items, v = 0;
15611         start = start || 0;
15612         end = (end || end === 0) ? end : rs.length-1;
15613
15614         for(var i = start; i <= end; i++){
15615             v += (rs[i].data[property] || 0);
15616         }
15617         return v;
15618     },
15619
15620     /**
15621      * Filter the records by a specified property.
15622      * @param {String} field A field on your records
15623      * @param {String/RegExp} value Either a string that the field
15624      * should start with or a RegExp to test against the field
15625      * @param {Boolean} anyMatch True to match any part not just the beginning
15626      */
15627     filter : function(property, value, anyMatch){
15628         var fn = this.createFilterFn(property, value, anyMatch);
15629         return fn ? this.filterBy(fn) : this.clearFilter();
15630     },
15631
15632     /**
15633      * Filter by a function. The specified function will be called with each
15634      * record in this data source. If the function returns true the record is included,
15635      * otherwise it is filtered.
15636      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15637      * @param {Object} scope (optional) The scope of the function (defaults to this)
15638      */
15639     filterBy : function(fn, scope){
15640         this.snapshot = this.snapshot || this.data;
15641         this.data = this.queryBy(fn, scope||this);
15642         this.fireEvent("datachanged", this);
15643     },
15644
15645     /**
15646      * Query the records by a specified property.
15647      * @param {String} field A field on your records
15648      * @param {String/RegExp} value Either a string that the field
15649      * should start with or a RegExp to test against the field
15650      * @param {Boolean} anyMatch True to match any part not just the beginning
15651      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15652      */
15653     query : function(property, value, anyMatch){
15654         var fn = this.createFilterFn(property, value, anyMatch);
15655         return fn ? this.queryBy(fn) : this.data.clone();
15656     },
15657
15658     /**
15659      * Query by a function. The specified function will be called with each
15660      * record in this data source. If the function returns true the record is included
15661      * in the results.
15662      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15663      * @param {Object} scope (optional) The scope of the function (defaults to this)
15664       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15665      **/
15666     queryBy : function(fn, scope){
15667         var data = this.snapshot || this.data;
15668         return data.filterBy(fn, scope||this);
15669     },
15670
15671     /**
15672      * Collects unique values for a particular dataIndex from this store.
15673      * @param {String} dataIndex The property to collect
15674      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15675      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15676      * @return {Array} An array of the unique values
15677      **/
15678     collect : function(dataIndex, allowNull, bypassFilter){
15679         var d = (bypassFilter === true && this.snapshot) ?
15680                 this.snapshot.items : this.data.items;
15681         var v, sv, r = [], l = {};
15682         for(var i = 0, len = d.length; i < len; i++){
15683             v = d[i].data[dataIndex];
15684             sv = String(v);
15685             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15686                 l[sv] = true;
15687                 r[r.length] = v;
15688             }
15689         }
15690         return r;
15691     },
15692
15693     /**
15694      * Revert to a view of the Record cache with no filtering applied.
15695      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15696      */
15697     clearFilter : function(suppressEvent){
15698         if(this.snapshot && this.snapshot != this.data){
15699             this.data = this.snapshot;
15700             delete this.snapshot;
15701             if(suppressEvent !== true){
15702                 this.fireEvent("datachanged", this);
15703             }
15704         }
15705     },
15706
15707     // private
15708     afterEdit : function(record){
15709         if(this.modified.indexOf(record) == -1){
15710             this.modified.push(record);
15711         }
15712         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15713     },
15714     
15715     // private
15716     afterReject : function(record){
15717         this.modified.remove(record);
15718         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15719     },
15720
15721     // private
15722     afterCommit : function(record){
15723         this.modified.remove(record);
15724         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15725     },
15726
15727     /**
15728      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15729      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15730      */
15731     commitChanges : function(){
15732         var m = this.modified.slice(0);
15733         this.modified = [];
15734         for(var i = 0, len = m.length; i < len; i++){
15735             m[i].commit();
15736         }
15737     },
15738
15739     /**
15740      * Cancel outstanding changes on all changed records.
15741      */
15742     rejectChanges : function(){
15743         var m = this.modified.slice(0);
15744         this.modified = [];
15745         for(var i = 0, len = m.length; i < len; i++){
15746             m[i].reject();
15747         }
15748     },
15749
15750     onMetaChange : function(meta, rtype, o){
15751         this.recordType = rtype;
15752         this.fields = rtype.prototype.fields;
15753         delete this.snapshot;
15754         this.sortInfo = meta.sortInfo || this.sortInfo;
15755         this.modified = [];
15756         this.fireEvent('metachange', this, this.reader.meta);
15757     },
15758     
15759     moveIndex : function(data, type)
15760     {
15761         var index = this.indexOf(data);
15762         
15763         var newIndex = index + type;
15764         
15765         this.remove(data);
15766         
15767         this.insert(newIndex, data);
15768         
15769     }
15770 });/*
15771  * Based on:
15772  * Ext JS Library 1.1.1
15773  * Copyright(c) 2006-2007, Ext JS, LLC.
15774  *
15775  * Originally Released Under LGPL - original licence link has changed is not relivant.
15776  *
15777  * Fork - LGPL
15778  * <script type="text/javascript">
15779  */
15780
15781 /**
15782  * @class Roo.data.SimpleStore
15783  * @extends Roo.data.Store
15784  * Small helper class to make creating Stores from Array data easier.
15785  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15786  * @cfg {Array} fields An array of field definition objects, or field name strings.
15787  * @cfg {Object} an existing reader (eg. copied from another store)
15788  * @cfg {Array} data The multi-dimensional array of data
15789  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15790  * @cfg {Roo.data.Reader} reader  [not-required] 
15791  * @constructor
15792  * @param {Object} config
15793  */
15794 Roo.data.SimpleStore = function(config)
15795 {
15796     Roo.data.SimpleStore.superclass.constructor.call(this, {
15797         isLocal : true,
15798         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15799                 id: config.id
15800             },
15801             Roo.data.Record.create(config.fields)
15802         ),
15803         proxy : new Roo.data.MemoryProxy(config.data)
15804     });
15805     this.load();
15806 };
15807 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15808  * Based on:
15809  * Ext JS Library 1.1.1
15810  * Copyright(c) 2006-2007, Ext JS, LLC.
15811  *
15812  * Originally Released Under LGPL - original licence link has changed is not relivant.
15813  *
15814  * Fork - LGPL
15815  * <script type="text/javascript">
15816  */
15817
15818 /**
15819 /**
15820  * @extends Roo.data.Store
15821  * @class Roo.data.JsonStore
15822  * Small helper class to make creating Stores for JSON data easier. <br/>
15823 <pre><code>
15824 var store = new Roo.data.JsonStore({
15825     url: 'get-images.php',
15826     root: 'images',
15827     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15828 });
15829 </code></pre>
15830  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15831  * JsonReader and HttpProxy (unless inline data is provided).</b>
15832  * @cfg {Array} fields An array of field definition objects, or field name strings.
15833  * @constructor
15834  * @param {Object} config
15835  */
15836 Roo.data.JsonStore = function(c){
15837     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15838         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15839         reader: new Roo.data.JsonReader(c, c.fields)
15840     }));
15841 };
15842 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15843  * Based on:
15844  * Ext JS Library 1.1.1
15845  * Copyright(c) 2006-2007, Ext JS, LLC.
15846  *
15847  * Originally Released Under LGPL - original licence link has changed is not relivant.
15848  *
15849  * Fork - LGPL
15850  * <script type="text/javascript">
15851  */
15852
15853  
15854 Roo.data.Field = function(config){
15855     if(typeof config == "string"){
15856         config = {name: config};
15857     }
15858     Roo.apply(this, config);
15859     
15860     if(!this.type){
15861         this.type = "auto";
15862     }
15863     
15864     var st = Roo.data.SortTypes;
15865     // named sortTypes are supported, here we look them up
15866     if(typeof this.sortType == "string"){
15867         this.sortType = st[this.sortType];
15868     }
15869     
15870     // set default sortType for strings and dates
15871     if(!this.sortType){
15872         switch(this.type){
15873             case "string":
15874                 this.sortType = st.asUCString;
15875                 break;
15876             case "date":
15877                 this.sortType = st.asDate;
15878                 break;
15879             default:
15880                 this.sortType = st.none;
15881         }
15882     }
15883
15884     // define once
15885     var stripRe = /[\$,%]/g;
15886
15887     // prebuilt conversion function for this field, instead of
15888     // switching every time we're reading a value
15889     if(!this.convert){
15890         var cv, dateFormat = this.dateFormat;
15891         switch(this.type){
15892             case "":
15893             case "auto":
15894             case undefined:
15895                 cv = function(v){ return v; };
15896                 break;
15897             case "string":
15898                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15899                 break;
15900             case "int":
15901                 cv = function(v){
15902                     return v !== undefined && v !== null && v !== '' ?
15903                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15904                     };
15905                 break;
15906             case "float":
15907                 cv = function(v){
15908                     return v !== undefined && v !== null && v !== '' ?
15909                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15910                     };
15911                 break;
15912             case "bool":
15913             case "boolean":
15914                 cv = function(v){ return v === true || v === "true" || v == 1; };
15915                 break;
15916             case "date":
15917                 cv = function(v){
15918                     if(!v){
15919                         return '';
15920                     }
15921                     if(v instanceof Date){
15922                         return v;
15923                     }
15924                     if(dateFormat){
15925                         if(dateFormat == "timestamp"){
15926                             return new Date(v*1000);
15927                         }
15928                         return Date.parseDate(v, dateFormat);
15929                     }
15930                     var parsed = Date.parse(v);
15931                     return parsed ? new Date(parsed) : null;
15932                 };
15933              break;
15934             
15935         }
15936         this.convert = cv;
15937     }
15938 };
15939
15940 Roo.data.Field.prototype = {
15941     dateFormat: null,
15942     defaultValue: "",
15943     mapping: null,
15944     sortType : null,
15945     sortDir : "ASC"
15946 };/*
15947  * Based on:
15948  * Ext JS Library 1.1.1
15949  * Copyright(c) 2006-2007, Ext JS, LLC.
15950  *
15951  * Originally Released Under LGPL - original licence link has changed is not relivant.
15952  *
15953  * Fork - LGPL
15954  * <script type="text/javascript">
15955  */
15956  
15957 // Base class for reading structured data from a data source.  This class is intended to be
15958 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15959
15960 /**
15961  * @class Roo.data.DataReader
15962  * @abstract
15963  * Base class for reading structured data from a data source.  This class is intended to be
15964  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15965  */
15966
15967 Roo.data.DataReader = function(meta, recordType){
15968     
15969     this.meta = meta;
15970     
15971     this.recordType = recordType instanceof Array ? 
15972         Roo.data.Record.create(recordType) : recordType;
15973 };
15974
15975 Roo.data.DataReader.prototype = {
15976     
15977     
15978     readerType : 'Data',
15979      /**
15980      * Create an empty record
15981      * @param {Object} data (optional) - overlay some values
15982      * @return {Roo.data.Record} record created.
15983      */
15984     newRow :  function(d) {
15985         var da =  {};
15986         this.recordType.prototype.fields.each(function(c) {
15987             switch( c.type) {
15988                 case 'int' : da[c.name] = 0; break;
15989                 case 'date' : da[c.name] = new Date(); break;
15990                 case 'float' : da[c.name] = 0.0; break;
15991                 case 'boolean' : da[c.name] = false; break;
15992                 default : da[c.name] = ""; break;
15993             }
15994             
15995         });
15996         return new this.recordType(Roo.apply(da, d));
15997     }
15998     
15999     
16000 };/*
16001  * Based on:
16002  * Ext JS Library 1.1.1
16003  * Copyright(c) 2006-2007, Ext JS, LLC.
16004  *
16005  * Originally Released Under LGPL - original licence link has changed is not relivant.
16006  *
16007  * Fork - LGPL
16008  * <script type="text/javascript">
16009  */
16010
16011 /**
16012  * @class Roo.data.DataProxy
16013  * @extends Roo.data.Observable
16014  * @abstract
16015  * This class is an abstract base class for implementations which provide retrieval of
16016  * unformatted data objects.<br>
16017  * <p>
16018  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16019  * (of the appropriate type which knows how to parse the data object) to provide a block of
16020  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16021  * <p>
16022  * Custom implementations must implement the load method as described in
16023  * {@link Roo.data.HttpProxy#load}.
16024  */
16025 Roo.data.DataProxy = function(){
16026     this.addEvents({
16027         /**
16028          * @event beforeload
16029          * Fires before a network request is made to retrieve a data object.
16030          * @param {Object} This DataProxy object.
16031          * @param {Object} params The params parameter to the load function.
16032          */
16033         beforeload : true,
16034         /**
16035          * @event load
16036          * Fires before the load method's callback is called.
16037          * @param {Object} This DataProxy object.
16038          * @param {Object} o The data object.
16039          * @param {Object} arg The callback argument object passed to the load function.
16040          */
16041         load : true,
16042         /**
16043          * @event loadexception
16044          * Fires if an Exception occurs during data retrieval.
16045          * @param {Object} This DataProxy object.
16046          * @param {Object} o The data object.
16047          * @param {Object} arg The callback argument object passed to the load function.
16048          * @param {Object} e The Exception.
16049          */
16050         loadexception : true
16051     });
16052     Roo.data.DataProxy.superclass.constructor.call(this);
16053 };
16054
16055 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16056
16057     /**
16058      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16059      */
16060 /*
16061  * Based on:
16062  * Ext JS Library 1.1.1
16063  * Copyright(c) 2006-2007, Ext JS, LLC.
16064  *
16065  * Originally Released Under LGPL - original licence link has changed is not relivant.
16066  *
16067  * Fork - LGPL
16068  * <script type="text/javascript">
16069  */
16070 /**
16071  * @class Roo.data.MemoryProxy
16072  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16073  * to the Reader when its load method is called.
16074  * @constructor
16075  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16076  */
16077 Roo.data.MemoryProxy = function(data){
16078     if (data.data) {
16079         data = data.data;
16080     }
16081     Roo.data.MemoryProxy.superclass.constructor.call(this);
16082     this.data = data;
16083 };
16084
16085 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16086     
16087     /**
16088      * Load data from the requested source (in this case an in-memory
16089      * data object passed to the constructor), read the data object into
16090      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16091      * process that block using the passed callback.
16092      * @param {Object} params This parameter is not used by the MemoryProxy class.
16093      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16094      * object into a block of Roo.data.Records.
16095      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16096      * The function must be passed <ul>
16097      * <li>The Record block object</li>
16098      * <li>The "arg" argument from the load function</li>
16099      * <li>A boolean success indicator</li>
16100      * </ul>
16101      * @param {Object} scope The scope in which to call the callback
16102      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16103      */
16104     load : function(params, reader, callback, scope, arg){
16105         params = params || {};
16106         var result;
16107         try {
16108             result = reader.readRecords(params.data ? params.data :this.data);
16109         }catch(e){
16110             this.fireEvent("loadexception", this, arg, null, e);
16111             callback.call(scope, null, arg, false);
16112             return;
16113         }
16114         callback.call(scope, result, arg, true);
16115     },
16116     
16117     // private
16118     update : function(params, records){
16119         
16120     }
16121 });/*
16122  * Based on:
16123  * Ext JS Library 1.1.1
16124  * Copyright(c) 2006-2007, Ext JS, LLC.
16125  *
16126  * Originally Released Under LGPL - original licence link has changed is not relivant.
16127  *
16128  * Fork - LGPL
16129  * <script type="text/javascript">
16130  */
16131 /**
16132  * @class Roo.data.HttpProxy
16133  * @extends Roo.data.DataProxy
16134  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16135  * configured to reference a certain URL.<br><br>
16136  * <p>
16137  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16138  * from which the running page was served.<br><br>
16139  * <p>
16140  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16141  * <p>
16142  * Be aware that to enable the browser to parse an XML document, the server must set
16143  * the Content-Type header in the HTTP response to "text/xml".
16144  * @constructor
16145  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16146  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16147  * will be used to make the request.
16148  */
16149 Roo.data.HttpProxy = function(conn){
16150     Roo.data.HttpProxy.superclass.constructor.call(this);
16151     // is conn a conn config or a real conn?
16152     this.conn = conn;
16153     this.useAjax = !conn || !conn.events;
16154   
16155 };
16156
16157 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16158     // thse are take from connection...
16159     
16160     /**
16161      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16162      */
16163     /**
16164      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16165      * extra parameters to each request made by this object. (defaults to undefined)
16166      */
16167     /**
16168      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16169      *  to each request made by this object. (defaults to undefined)
16170      */
16171     /**
16172      * @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)
16173      */
16174     /**
16175      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16176      */
16177      /**
16178      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16179      * @type Boolean
16180      */
16181   
16182
16183     /**
16184      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16185      * @type Boolean
16186      */
16187     /**
16188      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16189      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16190      * a finer-grained basis than the DataProxy events.
16191      */
16192     getConnection : function(){
16193         return this.useAjax ? Roo.Ajax : this.conn;
16194     },
16195
16196     /**
16197      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16198      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16199      * process that block using the passed callback.
16200      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16201      * for the request to the remote server.
16202      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16203      * object into a block of Roo.data.Records.
16204      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16205      * The function must be passed <ul>
16206      * <li>The Record block object</li>
16207      * <li>The "arg" argument from the load function</li>
16208      * <li>A boolean success indicator</li>
16209      * </ul>
16210      * @param {Object} scope The scope in which to call the callback
16211      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16212      */
16213     load : function(params, reader, callback, scope, arg){
16214         if(this.fireEvent("beforeload", this, params) !== false){
16215             var  o = {
16216                 params : params || {},
16217                 request: {
16218                     callback : callback,
16219                     scope : scope,
16220                     arg : arg
16221                 },
16222                 reader: reader,
16223                 callback : this.loadResponse,
16224                 scope: this
16225             };
16226             if(this.useAjax){
16227                 Roo.applyIf(o, this.conn);
16228                 if(this.activeRequest){
16229                     Roo.Ajax.abort(this.activeRequest);
16230                 }
16231                 this.activeRequest = Roo.Ajax.request(o);
16232             }else{
16233                 this.conn.request(o);
16234             }
16235         }else{
16236             callback.call(scope||this, null, arg, false);
16237         }
16238     },
16239
16240     // private
16241     loadResponse : function(o, success, response){
16242         delete this.activeRequest;
16243         if(!success){
16244             this.fireEvent("loadexception", this, o, response);
16245             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16246             return;
16247         }
16248         var result;
16249         try {
16250             result = o.reader.read(response);
16251         }catch(e){
16252             this.fireEvent("loadexception", this, o, response, e);
16253             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16254             return;
16255         }
16256         
16257         this.fireEvent("load", this, o, o.request.arg);
16258         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16259     },
16260
16261     // private
16262     update : function(dataSet){
16263
16264     },
16265
16266     // private
16267     updateResponse : function(dataSet){
16268
16269     }
16270 });/*
16271  * Based on:
16272  * Ext JS Library 1.1.1
16273  * Copyright(c) 2006-2007, Ext JS, LLC.
16274  *
16275  * Originally Released Under LGPL - original licence link has changed is not relivant.
16276  *
16277  * Fork - LGPL
16278  * <script type="text/javascript">
16279  */
16280
16281 /**
16282  * @class Roo.data.ScriptTagProxy
16283  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16284  * other than the originating domain of the running page.<br><br>
16285  * <p>
16286  * <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
16287  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16288  * <p>
16289  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16290  * source code that is used as the source inside a &lt;script> tag.<br><br>
16291  * <p>
16292  * In order for the browser to process the returned data, the server must wrap the data object
16293  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16294  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16295  * depending on whether the callback name was passed:
16296  * <p>
16297  * <pre><code>
16298 boolean scriptTag = false;
16299 String cb = request.getParameter("callback");
16300 if (cb != null) {
16301     scriptTag = true;
16302     response.setContentType("text/javascript");
16303 } else {
16304     response.setContentType("application/x-json");
16305 }
16306 Writer out = response.getWriter();
16307 if (scriptTag) {
16308     out.write(cb + "(");
16309 }
16310 out.print(dataBlock.toJsonString());
16311 if (scriptTag) {
16312     out.write(");");
16313 }
16314 </pre></code>
16315  *
16316  * @constructor
16317  * @param {Object} config A configuration object.
16318  */
16319 Roo.data.ScriptTagProxy = function(config){
16320     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16321     Roo.apply(this, config);
16322     this.head = document.getElementsByTagName("head")[0];
16323 };
16324
16325 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16326
16327 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16328     /**
16329      * @cfg {String} url The URL from which to request the data object.
16330      */
16331     /**
16332      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16333      */
16334     timeout : 30000,
16335     /**
16336      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16337      * the server the name of the callback function set up by the load call to process the returned data object.
16338      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16339      * javascript output which calls this named function passing the data object as its only parameter.
16340      */
16341     callbackParam : "callback",
16342     /**
16343      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16344      * name to the request.
16345      */
16346     nocache : true,
16347
16348     /**
16349      * Load data from the configured URL, read the data object into
16350      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16351      * process that block using the passed callback.
16352      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16353      * for the request to the remote server.
16354      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16355      * object into a block of Roo.data.Records.
16356      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16357      * The function must be passed <ul>
16358      * <li>The Record block object</li>
16359      * <li>The "arg" argument from the load function</li>
16360      * <li>A boolean success indicator</li>
16361      * </ul>
16362      * @param {Object} scope The scope in which to call the callback
16363      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16364      */
16365     load : function(params, reader, callback, scope, arg){
16366         if(this.fireEvent("beforeload", this, params) !== false){
16367
16368             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16369
16370             var url = this.url;
16371             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16372             if(this.nocache){
16373                 url += "&_dc=" + (new Date().getTime());
16374             }
16375             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16376             var trans = {
16377                 id : transId,
16378                 cb : "stcCallback"+transId,
16379                 scriptId : "stcScript"+transId,
16380                 params : params,
16381                 arg : arg,
16382                 url : url,
16383                 callback : callback,
16384                 scope : scope,
16385                 reader : reader
16386             };
16387             var conn = this;
16388
16389             window[trans.cb] = function(o){
16390                 conn.handleResponse(o, trans);
16391             };
16392
16393             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16394
16395             if(this.autoAbort !== false){
16396                 this.abort();
16397             }
16398
16399             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16400
16401             var script = document.createElement("script");
16402             script.setAttribute("src", url);
16403             script.setAttribute("type", "text/javascript");
16404             script.setAttribute("id", trans.scriptId);
16405             this.head.appendChild(script);
16406
16407             this.trans = trans;
16408         }else{
16409             callback.call(scope||this, null, arg, false);
16410         }
16411     },
16412
16413     // private
16414     isLoading : function(){
16415         return this.trans ? true : false;
16416     },
16417
16418     /**
16419      * Abort the current server request.
16420      */
16421     abort : function(){
16422         if(this.isLoading()){
16423             this.destroyTrans(this.trans);
16424         }
16425     },
16426
16427     // private
16428     destroyTrans : function(trans, isLoaded){
16429         this.head.removeChild(document.getElementById(trans.scriptId));
16430         clearTimeout(trans.timeoutId);
16431         if(isLoaded){
16432             window[trans.cb] = undefined;
16433             try{
16434                 delete window[trans.cb];
16435             }catch(e){}
16436         }else{
16437             // if hasn't been loaded, wait for load to remove it to prevent script error
16438             window[trans.cb] = function(){
16439                 window[trans.cb] = undefined;
16440                 try{
16441                     delete window[trans.cb];
16442                 }catch(e){}
16443             };
16444         }
16445     },
16446
16447     // private
16448     handleResponse : function(o, trans){
16449         this.trans = false;
16450         this.destroyTrans(trans, true);
16451         var result;
16452         try {
16453             result = trans.reader.readRecords(o);
16454         }catch(e){
16455             this.fireEvent("loadexception", this, o, trans.arg, e);
16456             trans.callback.call(trans.scope||window, null, trans.arg, false);
16457             return;
16458         }
16459         this.fireEvent("load", this, o, trans.arg);
16460         trans.callback.call(trans.scope||window, result, trans.arg, true);
16461     },
16462
16463     // private
16464     handleFailure : function(trans){
16465         this.trans = false;
16466         this.destroyTrans(trans, false);
16467         this.fireEvent("loadexception", this, null, trans.arg);
16468         trans.callback.call(trans.scope||window, null, trans.arg, false);
16469     }
16470 });/*
16471  * Based on:
16472  * Ext JS Library 1.1.1
16473  * Copyright(c) 2006-2007, Ext JS, LLC.
16474  *
16475  * Originally Released Under LGPL - original licence link has changed is not relivant.
16476  *
16477  * Fork - LGPL
16478  * <script type="text/javascript">
16479  */
16480
16481 /**
16482  * @class Roo.data.JsonReader
16483  * @extends Roo.data.DataReader
16484  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16485  * based on mappings in a provided Roo.data.Record constructor.
16486  * 
16487  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16488  * in the reply previously. 
16489  * 
16490  * <p>
16491  * Example code:
16492  * <pre><code>
16493 var RecordDef = Roo.data.Record.create([
16494     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16495     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16496 ]);
16497 var myReader = new Roo.data.JsonReader({
16498     totalProperty: "results",    // The property which contains the total dataset size (optional)
16499     root: "rows",                // The property which contains an Array of row objects
16500     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16501 }, RecordDef);
16502 </code></pre>
16503  * <p>
16504  * This would consume a JSON file like this:
16505  * <pre><code>
16506 { 'results': 2, 'rows': [
16507     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16508     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16509 }
16510 </code></pre>
16511  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16512  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16513  * paged from the remote server.
16514  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16515  * @cfg {String} root name of the property which contains the Array of row objects.
16516  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16517  * @cfg {Array} fields Array of field definition objects
16518  * @constructor
16519  * Create a new JsonReader
16520  * @param {Object} meta Metadata configuration options
16521  * @param {Object} recordType Either an Array of field definition objects,
16522  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16523  */
16524 Roo.data.JsonReader = function(meta, recordType){
16525     
16526     meta = meta || {};
16527     // set some defaults:
16528     Roo.applyIf(meta, {
16529         totalProperty: 'total',
16530         successProperty : 'success',
16531         root : 'data',
16532         id : 'id'
16533     });
16534     
16535     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16536 };
16537 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16538     
16539     readerType : 'Json',
16540     
16541     /**
16542      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16543      * Used by Store query builder to append _requestMeta to params.
16544      * 
16545      */
16546     metaFromRemote : false,
16547     /**
16548      * This method is only used by a DataProxy which has retrieved data from a remote server.
16549      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16550      * @return {Object} data A data block which is used by an Roo.data.Store object as
16551      * a cache of Roo.data.Records.
16552      */
16553     read : function(response){
16554         var json = response.responseText;
16555        
16556         var o = /* eval:var:o */ eval("("+json+")");
16557         if(!o) {
16558             throw {message: "JsonReader.read: Json object not found"};
16559         }
16560         
16561         if(o.metaData){
16562             
16563             delete this.ef;
16564             this.metaFromRemote = true;
16565             this.meta = o.metaData;
16566             this.recordType = Roo.data.Record.create(o.metaData.fields);
16567             this.onMetaChange(this.meta, this.recordType, o);
16568         }
16569         return this.readRecords(o);
16570     },
16571
16572     // private function a store will implement
16573     onMetaChange : function(meta, recordType, o){
16574
16575     },
16576
16577     /**
16578          * @ignore
16579          */
16580     simpleAccess: function(obj, subsc) {
16581         return obj[subsc];
16582     },
16583
16584         /**
16585          * @ignore
16586          */
16587     getJsonAccessor: function(){
16588         var re = /[\[\.]/;
16589         return function(expr) {
16590             try {
16591                 return(re.test(expr))
16592                     ? new Function("obj", "return obj." + expr)
16593                     : function(obj){
16594                         return obj[expr];
16595                     };
16596             } catch(e){}
16597             return Roo.emptyFn;
16598         };
16599     }(),
16600
16601     /**
16602      * Create a data block containing Roo.data.Records from an XML document.
16603      * @param {Object} o An object which contains an Array of row objects in the property specified
16604      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16605      * which contains the total size of the dataset.
16606      * @return {Object} data A data block which is used by an Roo.data.Store object as
16607      * a cache of Roo.data.Records.
16608      */
16609     readRecords : function(o){
16610         /**
16611          * After any data loads, the raw JSON data is available for further custom processing.
16612          * @type Object
16613          */
16614         this.o = o;
16615         var s = this.meta, Record = this.recordType,
16616             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16617
16618 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16619         if (!this.ef) {
16620             if(s.totalProperty) {
16621                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16622                 }
16623                 if(s.successProperty) {
16624                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16625                 }
16626                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16627                 if (s.id) {
16628                         var g = this.getJsonAccessor(s.id);
16629                         this.getId = function(rec) {
16630                                 var r = g(rec);  
16631                                 return (r === undefined || r === "") ? null : r;
16632                         };
16633                 } else {
16634                         this.getId = function(){return null;};
16635                 }
16636             this.ef = [];
16637             for(var jj = 0; jj < fl; jj++){
16638                 f = fi[jj];
16639                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16640                 this.ef[jj] = this.getJsonAccessor(map);
16641             }
16642         }
16643
16644         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16645         if(s.totalProperty){
16646             var vt = parseInt(this.getTotal(o), 10);
16647             if(!isNaN(vt)){
16648                 totalRecords = vt;
16649             }
16650         }
16651         if(s.successProperty){
16652             var vs = this.getSuccess(o);
16653             if(vs === false || vs === 'false'){
16654                 success = false;
16655             }
16656         }
16657         var records = [];
16658         for(var i = 0; i < c; i++){
16659                 var n = root[i];
16660             var values = {};
16661             var id = this.getId(n);
16662             for(var j = 0; j < fl; j++){
16663                 f = fi[j];
16664             var v = this.ef[j](n);
16665             if (!f.convert) {
16666                 Roo.log('missing convert for ' + f.name);
16667                 Roo.log(f);
16668                 continue;
16669             }
16670             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16671             }
16672             var record = new Record(values, id);
16673             record.json = n;
16674             records[i] = record;
16675         }
16676         return {
16677             raw : o,
16678             success : success,
16679             records : records,
16680             totalRecords : totalRecords
16681         };
16682     },
16683     // used when loading children.. @see loadDataFromChildren
16684     toLoadData: function(rec)
16685     {
16686         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16687         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16688         return { data : data, total : data.length };
16689         
16690     }
16691 });/*
16692  * Based on:
16693  * Ext JS Library 1.1.1
16694  * Copyright(c) 2006-2007, Ext JS, LLC.
16695  *
16696  * Originally Released Under LGPL - original licence link has changed is not relivant.
16697  *
16698  * Fork - LGPL
16699  * <script type="text/javascript">
16700  */
16701
16702 /**
16703  * @class Roo.data.ArrayReader
16704  * @extends Roo.data.DataReader
16705  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16706  * Each element of that Array represents a row of data fields. The
16707  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16708  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16709  * <p>
16710  * Example code:.
16711  * <pre><code>
16712 var RecordDef = Roo.data.Record.create([
16713     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16714     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16715 ]);
16716 var myReader = new Roo.data.ArrayReader({
16717     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16718 }, RecordDef);
16719 </code></pre>
16720  * <p>
16721  * This would consume an Array like this:
16722  * <pre><code>
16723 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16724   </code></pre>
16725  
16726  * @constructor
16727  * Create a new JsonReader
16728  * @param {Object} meta Metadata configuration options.
16729  * @param {Object|Array} recordType Either an Array of field definition objects
16730  * 
16731  * @cfg {Array} fields Array of field definition objects
16732  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16733  * as specified to {@link Roo.data.Record#create},
16734  * or an {@link Roo.data.Record} object
16735  *
16736  * 
16737  * created using {@link Roo.data.Record#create}.
16738  */
16739 Roo.data.ArrayReader = function(meta, recordType)
16740 {    
16741     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16742 };
16743
16744 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16745     
16746       /**
16747      * Create a data block containing Roo.data.Records from an XML document.
16748      * @param {Object} o An Array of row objects which represents the dataset.
16749      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16750      * a cache of Roo.data.Records.
16751      */
16752     readRecords : function(o)
16753     {
16754         var sid = this.meta ? this.meta.id : null;
16755         var recordType = this.recordType, fields = recordType.prototype.fields;
16756         var records = [];
16757         var root = o;
16758         for(var i = 0; i < root.length; i++){
16759             var n = root[i];
16760             var values = {};
16761             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16762             for(var j = 0, jlen = fields.length; j < jlen; j++){
16763                 var f = fields.items[j];
16764                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16765                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16766                 v = f.convert(v);
16767                 values[f.name] = v;
16768             }
16769             var record = new recordType(values, id);
16770             record.json = n;
16771             records[records.length] = record;
16772         }
16773         return {
16774             records : records,
16775             totalRecords : records.length
16776         };
16777     },
16778     // used when loading children.. @see loadDataFromChildren
16779     toLoadData: function(rec)
16780     {
16781         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16782         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16783         
16784     }
16785     
16786     
16787 });/*
16788  * - LGPL
16789  * * 
16790  */
16791
16792 /**
16793  * @class Roo.bootstrap.ComboBox
16794  * @extends Roo.bootstrap.TriggerField
16795  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16796  * @cfg {Boolean} append (true|false) default false
16797  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16798  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16799  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16800  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16801  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16802  * @cfg {Boolean} animate default true
16803  * @cfg {Boolean} emptyResultText only for touch device
16804  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16805  * @cfg {String} emptyTitle default ''
16806  * @cfg {Number} width fixed with? experimental
16807  * @constructor
16808  * Create a new ComboBox.
16809  * @param {Object} config Configuration options
16810  */
16811 Roo.bootstrap.ComboBox = function(config){
16812     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16813     this.addEvents({
16814         /**
16815          * @event expand
16816          * Fires when the dropdown list is expanded
16817         * @param {Roo.bootstrap.ComboBox} combo This combo box
16818         */
16819         'expand' : true,
16820         /**
16821          * @event collapse
16822          * Fires when the dropdown list is collapsed
16823         * @param {Roo.bootstrap.ComboBox} combo This combo box
16824         */
16825         'collapse' : true,
16826         /**
16827          * @event beforeselect
16828          * Fires before a list item is selected. Return false to cancel the selection.
16829         * @param {Roo.bootstrap.ComboBox} combo This combo box
16830         * @param {Roo.data.Record} record The data record returned from the underlying store
16831         * @param {Number} index The index of the selected item in the dropdown list
16832         */
16833         'beforeselect' : true,
16834         /**
16835          * @event select
16836          * Fires when a list item is selected
16837         * @param {Roo.bootstrap.ComboBox} combo This combo box
16838         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16839         * @param {Number} index The index of the selected item in the dropdown list
16840         */
16841         'select' : true,
16842         /**
16843          * @event beforequery
16844          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16845          * The event object passed has these properties:
16846         * @param {Roo.bootstrap.ComboBox} combo This combo box
16847         * @param {String} query The query
16848         * @param {Boolean} forceAll true to force "all" query
16849         * @param {Boolean} cancel true to cancel the query
16850         * @param {Object} e The query event object
16851         */
16852         'beforequery': true,
16853          /**
16854          * @event add
16855          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16856         * @param {Roo.bootstrap.ComboBox} combo This combo box
16857         */
16858         'add' : true,
16859         /**
16860          * @event edit
16861          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16862         * @param {Roo.bootstrap.ComboBox} combo This combo box
16863         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16864         */
16865         'edit' : true,
16866         /**
16867          * @event remove
16868          * Fires when the remove value from the combobox array
16869         * @param {Roo.bootstrap.ComboBox} combo This combo box
16870         */
16871         'remove' : true,
16872         /**
16873          * @event afterremove
16874          * Fires when the remove value from the combobox array
16875         * @param {Roo.bootstrap.ComboBox} combo This combo box
16876         */
16877         'afterremove' : true,
16878         /**
16879          * @event specialfilter
16880          * Fires when specialfilter
16881             * @param {Roo.bootstrap.ComboBox} combo This combo box
16882             */
16883         'specialfilter' : true,
16884         /**
16885          * @event tick
16886          * Fires when tick the element
16887             * @param {Roo.bootstrap.ComboBox} combo This combo box
16888             */
16889         'tick' : true,
16890         /**
16891          * @event touchviewdisplay
16892          * Fires when touch view require special display (default is using displayField)
16893             * @param {Roo.bootstrap.ComboBox} combo This combo box
16894             * @param {Object} cfg set html .
16895             */
16896         'touchviewdisplay' : true
16897         
16898     });
16899     
16900     this.item = [];
16901     this.tickItems = [];
16902     
16903     this.selectedIndex = -1;
16904     if(this.mode == 'local'){
16905         if(config.queryDelay === undefined){
16906             this.queryDelay = 10;
16907         }
16908         if(config.minChars === undefined){
16909             this.minChars = 0;
16910         }
16911     }
16912 };
16913
16914 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16915      
16916     /**
16917      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16918      * rendering into an Roo.Editor, defaults to false)
16919      */
16920     /**
16921      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16922      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16923      */
16924     /**
16925      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16926      */
16927     /**
16928      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16929      * the dropdown list (defaults to undefined, with no header element)
16930      */
16931
16932      /**
16933      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16934      */
16935      
16936      /**
16937      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16938      */
16939     listWidth: undefined,
16940     /**
16941      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16942      * mode = 'remote' or 'text' if mode = 'local')
16943      */
16944     displayField: undefined,
16945     
16946     /**
16947      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16948      * mode = 'remote' or 'value' if mode = 'local'). 
16949      * Note: use of a valueField requires the user make a selection
16950      * in order for a value to be mapped.
16951      */
16952     valueField: undefined,
16953     /**
16954      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16955      */
16956     modalTitle : '',
16957     
16958     /**
16959      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16960      * field's data value (defaults to the underlying DOM element's name)
16961      */
16962     hiddenName: undefined,
16963     /**
16964      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16965      */
16966     listClass: '',
16967     /**
16968      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16969      */
16970     selectedClass: 'active',
16971     
16972     /**
16973      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16974      */
16975     shadow:'sides',
16976     /**
16977      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16978      * anchor positions (defaults to 'tl-bl')
16979      */
16980     listAlign: 'tl-bl?',
16981     /**
16982      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16983      */
16984     maxHeight: 300,
16985     /**
16986      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16987      * query specified by the allQuery config option (defaults to 'query')
16988      */
16989     triggerAction: 'query',
16990     /**
16991      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16992      * (defaults to 4, does not apply if editable = false)
16993      */
16994     minChars : 4,
16995     /**
16996      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16997      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16998      */
16999     typeAhead: false,
17000     /**
17001      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17002      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17003      */
17004     queryDelay: 500,
17005     /**
17006      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17007      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17008      */
17009     pageSize: 0,
17010     /**
17011      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17012      * when editable = true (defaults to false)
17013      */
17014     selectOnFocus:false,
17015     /**
17016      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17017      */
17018     queryParam: 'query',
17019     /**
17020      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17021      * when mode = 'remote' (defaults to 'Loading...')
17022      */
17023     loadingText: 'Loading...',
17024     /**
17025      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17026      */
17027     resizable: false,
17028     /**
17029      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17030      */
17031     handleHeight : 8,
17032     /**
17033      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17034      * traditional select (defaults to true)
17035      */
17036     editable: true,
17037     /**
17038      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17039      */
17040     allQuery: '',
17041     /**
17042      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17043      */
17044     mode: 'remote',
17045     /**
17046      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17047      * listWidth has a higher value)
17048      */
17049     minListWidth : 70,
17050     /**
17051      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17052      * allow the user to set arbitrary text into the field (defaults to false)
17053      */
17054     forceSelection:false,
17055     /**
17056      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17057      * if typeAhead = true (defaults to 250)
17058      */
17059     typeAheadDelay : 250,
17060     /**
17061      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17062      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17063      */
17064     valueNotFoundText : undefined,
17065     /**
17066      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17067      */
17068     blockFocus : false,
17069     
17070     /**
17071      * @cfg {Boolean} disableClear Disable showing of clear button.
17072      */
17073     disableClear : false,
17074     /**
17075      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17076      */
17077     alwaysQuery : false,
17078     
17079     /**
17080      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17081      */
17082     multiple : false,
17083     
17084     /**
17085      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17086      */
17087     invalidClass : "has-warning",
17088     
17089     /**
17090      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17091      */
17092     validClass : "has-success",
17093     
17094     /**
17095      * @cfg {Boolean} specialFilter (true|false) special filter default false
17096      */
17097     specialFilter : false,
17098     
17099     /**
17100      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17101      */
17102     mobileTouchView : true,
17103     
17104     /**
17105      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17106      */
17107     useNativeIOS : false,
17108     
17109     /**
17110      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17111      */
17112     mobile_restrict_height : false,
17113     
17114     ios_options : false,
17115     
17116     //private
17117     addicon : false,
17118     editicon: false,
17119     
17120     page: 0,
17121     hasQuery: false,
17122     append: false,
17123     loadNext: false,
17124     autoFocus : true,
17125     tickable : false,
17126     btnPosition : 'right',
17127     triggerList : true,
17128     showToggleBtn : true,
17129     animate : true,
17130     emptyResultText: 'Empty',
17131     triggerText : 'Select',
17132     emptyTitle : '',
17133     width : false,
17134     
17135     // element that contains real text value.. (when hidden is used..)
17136     
17137     getAutoCreate : function()
17138     {   
17139         var cfg = false;
17140         //render
17141         /*
17142          * Render classic select for iso
17143          */
17144         
17145         if(Roo.isIOS && this.useNativeIOS){
17146             cfg = this.getAutoCreateNativeIOS();
17147             return cfg;
17148         }
17149         
17150         /*
17151          * Touch Devices
17152          */
17153         
17154         if(Roo.isTouch && this.mobileTouchView){
17155             cfg = this.getAutoCreateTouchView();
17156             return cfg;;
17157         }
17158         
17159         /*
17160          *  Normal ComboBox
17161          */
17162         if(!this.tickable){
17163             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
17164             return cfg;
17165         }
17166         
17167         /*
17168          *  ComboBox with tickable selections
17169          */
17170              
17171         var align = this.labelAlign || this.parentLabelAlign();
17172         
17173         cfg = {
17174             cls : 'form-group roo-combobox-tickable' //input-group
17175         };
17176         
17177         var btn_text_select = '';
17178         var btn_text_done = '';
17179         var btn_text_cancel = '';
17180         
17181         if (this.btn_text_show) {
17182             btn_text_select = 'Select';
17183             btn_text_done = 'Done';
17184             btn_text_cancel = 'Cancel'; 
17185         }
17186         
17187         var buttons = {
17188             tag : 'div',
17189             cls : 'tickable-buttons',
17190             cn : [
17191                 {
17192                     tag : 'button',
17193                     type : 'button',
17194                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17195                     //html : this.triggerText
17196                     html: btn_text_select
17197                 },
17198                 {
17199                     tag : 'button',
17200                     type : 'button',
17201                     name : 'ok',
17202                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17203                     //html : 'Done'
17204                     html: btn_text_done
17205                 },
17206                 {
17207                     tag : 'button',
17208                     type : 'button',
17209                     name : 'cancel',
17210                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17211                     //html : 'Cancel'
17212                     html: btn_text_cancel
17213                 }
17214             ]
17215         };
17216         
17217         if(this.editable){
17218             buttons.cn.unshift({
17219                 tag: 'input',
17220                 cls: 'roo-select2-search-field-input'
17221             });
17222         }
17223         
17224         var _this = this;
17225         
17226         Roo.each(buttons.cn, function(c){
17227             if (_this.size) {
17228                 c.cls += ' btn-' + _this.size;
17229             }
17230
17231             if (_this.disabled) {
17232                 c.disabled = true;
17233             }
17234         });
17235         
17236         var box = {
17237             tag: 'div',
17238             style : 'display: contents',
17239             cn: [
17240                 {
17241                     tag: 'input',
17242                     type : 'hidden',
17243                     cls: 'form-hidden-field'
17244                 },
17245                 {
17246                     tag: 'ul',
17247                     cls: 'roo-select2-choices',
17248                     cn:[
17249                         {
17250                             tag: 'li',
17251                             cls: 'roo-select2-search-field',
17252                             cn: [
17253                                 buttons
17254                             ]
17255                         }
17256                     ]
17257                 }
17258             ]
17259         };
17260         
17261         var combobox = {
17262             cls: 'roo-select2-container input-group roo-select2-container-multi',
17263             cn: [
17264                 
17265                 box
17266 //                {
17267 //                    tag: 'ul',
17268 //                    cls: 'typeahead typeahead-long dropdown-menu',
17269 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17270 //                }
17271             ]
17272         };
17273         
17274         if(this.hasFeedback && !this.allowBlank){
17275             
17276             var feedback = {
17277                 tag: 'span',
17278                 cls: 'glyphicon form-control-feedback'
17279             };
17280
17281             combobox.cn.push(feedback);
17282         }
17283         
17284         
17285         
17286         var indicator = {
17287             tag : 'i',
17288             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17289             tooltip : 'This field is required'
17290         };
17291         if (Roo.bootstrap.version == 4) {
17292             indicator = {
17293                 tag : 'i',
17294                 style : 'display:none'
17295             };
17296         }
17297         if (align ==='left' && this.fieldLabel.length) {
17298             
17299             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17300             
17301             cfg.cn = [
17302                 indicator,
17303                 {
17304                     tag: 'label',
17305                     'for' :  id,
17306                     cls : 'control-label col-form-label',
17307                     html : this.fieldLabel
17308
17309                 },
17310                 {
17311                     cls : "", 
17312                     cn: [
17313                         combobox
17314                     ]
17315                 }
17316
17317             ];
17318             
17319             var labelCfg = cfg.cn[1];
17320             var contentCfg = cfg.cn[2];
17321             
17322
17323             if(this.indicatorpos == 'right'){
17324                 
17325                 cfg.cn = [
17326                     {
17327                         tag: 'label',
17328                         'for' :  id,
17329                         cls : 'control-label col-form-label',
17330                         cn : [
17331                             {
17332                                 tag : 'span',
17333                                 html : this.fieldLabel
17334                             },
17335                             indicator
17336                         ]
17337                     },
17338                     {
17339                         cls : "",
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344
17345                 ];
17346                 
17347                 
17348                 
17349                 labelCfg = cfg.cn[0];
17350                 contentCfg = cfg.cn[1];
17351             
17352             }
17353             
17354             if(this.labelWidth > 12){
17355                 labelCfg.style = "width: " + this.labelWidth + 'px';
17356             }
17357             if(this.width * 1 > 0){
17358                 contentCfg.style = "width: " + this.width + 'px';
17359             }
17360             if(this.labelWidth < 13 && this.labelmd == 0){
17361                 this.labelmd = this.labelWidth;
17362             }
17363             
17364             if(this.labellg > 0){
17365                 labelCfg.cls += ' col-lg-' + this.labellg;
17366                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17367             }
17368             
17369             if(this.labelmd > 0){
17370                 labelCfg.cls += ' col-md-' + this.labelmd;
17371                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17372             }
17373             
17374             if(this.labelsm > 0){
17375                 labelCfg.cls += ' col-sm-' + this.labelsm;
17376                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17377             }
17378             
17379             if(this.labelxs > 0){
17380                 labelCfg.cls += ' col-xs-' + this.labelxs;
17381                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17382             }
17383                 
17384                 
17385         } else if ( this.fieldLabel.length) {
17386 //                Roo.log(" label");
17387                  cfg.cn = [
17388                    indicator,
17389                     {
17390                         tag: 'label',
17391                         //cls : 'input-group-addon',
17392                         html : this.fieldLabel
17393                     },
17394                     combobox
17395                 ];
17396                 
17397                 if(this.indicatorpos == 'right'){
17398                     cfg.cn = [
17399                         {
17400                             tag: 'label',
17401                             //cls : 'input-group-addon',
17402                             html : this.fieldLabel
17403                         },
17404                         indicator,
17405                         combobox
17406                     ];
17407                     
17408                 }
17409
17410         } else {
17411             
17412 //                Roo.log(" no label && no align");
17413                 cfg = combobox
17414                      
17415                 
17416         }
17417          
17418         var settings=this;
17419         ['xs','sm','md','lg'].map(function(size){
17420             if (settings[size]) {
17421                 cfg.cls += ' col-' + size + '-' + settings[size];
17422             }
17423         });
17424         
17425         return cfg;
17426         
17427     },
17428     
17429     _initEventsCalled : false,
17430     
17431     // private
17432     initEvents: function()
17433     {   
17434         if (this._initEventsCalled) { // as we call render... prevent looping...
17435             return;
17436         }
17437         this._initEventsCalled = true;
17438         
17439         if (!this.store) {
17440             throw "can not find store for combo";
17441         }
17442         
17443         this.indicator = this.indicatorEl();
17444         
17445         this.store = Roo.factory(this.store, Roo.data);
17446         this.store.parent = this;
17447         
17448         // if we are building from html. then this element is so complex, that we can not really
17449         // use the rendered HTML.
17450         // so we have to trash and replace the previous code.
17451         if (Roo.XComponent.build_from_html) {
17452             // remove this element....
17453             var e = this.el.dom, k=0;
17454             while (e ) { e = e.previousSibling;  ++k;}
17455
17456             this.el.remove();
17457             
17458             this.el=false;
17459             this.rendered = false;
17460             
17461             this.render(this.parent().getChildContainer(true), k);
17462         }
17463         
17464         if(Roo.isIOS && this.useNativeIOS){
17465             this.initIOSView();
17466             return;
17467         }
17468         
17469         /*
17470          * Touch Devices
17471          */
17472         
17473         if(Roo.isTouch && this.mobileTouchView){
17474             this.initTouchView();
17475             return;
17476         }
17477         
17478         if(this.tickable){
17479             this.initTickableEvents();
17480             return;
17481         }
17482         
17483         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17484         
17485         if(this.hiddenName){
17486             
17487             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17488             
17489             this.hiddenField.dom.value =
17490                 this.hiddenValue !== undefined ? this.hiddenValue :
17491                 this.value !== undefined ? this.value : '';
17492
17493             // prevent input submission
17494             this.el.dom.removeAttribute('name');
17495             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17496              
17497              
17498         }
17499         //if(Roo.isGecko){
17500         //    this.el.dom.setAttribute('autocomplete', 'off');
17501         //}
17502         
17503         var cls = 'x-combo-list';
17504         
17505         //this.list = new Roo.Layer({
17506         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17507         //});
17508         
17509         var _this = this;
17510         
17511         (function(){
17512             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17513             _this.list.setWidth(lw);
17514         }).defer(100);
17515         
17516         this.list.on('mouseover', this.onViewOver, this);
17517         this.list.on('mousemove', this.onViewMove, this);
17518         this.list.on('scroll', this.onViewScroll, this);
17519         
17520         /*
17521         this.list.swallowEvent('mousewheel');
17522         this.assetHeight = 0;
17523
17524         if(this.title){
17525             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17526             this.assetHeight += this.header.getHeight();
17527         }
17528
17529         this.innerList = this.list.createChild({cls:cls+'-inner'});
17530         this.innerList.on('mouseover', this.onViewOver, this);
17531         this.innerList.on('mousemove', this.onViewMove, this);
17532         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17533         
17534         if(this.allowBlank && !this.pageSize && !this.disableClear){
17535             this.footer = this.list.createChild({cls:cls+'-ft'});
17536             this.pageTb = new Roo.Toolbar(this.footer);
17537            
17538         }
17539         if(this.pageSize){
17540             this.footer = this.list.createChild({cls:cls+'-ft'});
17541             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17542                     {pageSize: this.pageSize});
17543             
17544         }
17545         
17546         if (this.pageTb && this.allowBlank && !this.disableClear) {
17547             var _this = this;
17548             this.pageTb.add(new Roo.Toolbar.Fill(), {
17549                 cls: 'x-btn-icon x-btn-clear',
17550                 text: '&#160;',
17551                 handler: function()
17552                 {
17553                     _this.collapse();
17554                     _this.clearValue();
17555                     _this.onSelect(false, -1);
17556                 }
17557             });
17558         }
17559         if (this.footer) {
17560             this.assetHeight += this.footer.getHeight();
17561         }
17562         */
17563             
17564         if(!this.tpl){
17565             this.tpl = Roo.bootstrap.version == 4 ?
17566                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17567                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17568         }
17569
17570         this.view = new Roo.View(this.list, this.tpl, {
17571             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17572         });
17573         //this.view.wrapEl.setDisplayed(false);
17574         this.view.on('click', this.onViewClick, this);
17575         
17576         
17577         this.store.on('beforeload', this.onBeforeLoad, this);
17578         this.store.on('load', this.onLoad, this);
17579         this.store.on('loadexception', this.onLoadException, this);
17580         /*
17581         if(this.resizable){
17582             this.resizer = new Roo.Resizable(this.list,  {
17583                pinned:true, handles:'se'
17584             });
17585             this.resizer.on('resize', function(r, w, h){
17586                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17587                 this.listWidth = w;
17588                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17589                 this.restrictHeight();
17590             }, this);
17591             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17592         }
17593         */
17594         if(!this.editable){
17595             this.editable = true;
17596             this.setEditable(false);
17597         }
17598         
17599         /*
17600         
17601         if (typeof(this.events.add.listeners) != 'undefined') {
17602             
17603             this.addicon = this.wrap.createChild(
17604                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17605        
17606             this.addicon.on('click', function(e) {
17607                 this.fireEvent('add', this);
17608             }, this);
17609         }
17610         if (typeof(this.events.edit.listeners) != 'undefined') {
17611             
17612             this.editicon = this.wrap.createChild(
17613                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17614             if (this.addicon) {
17615                 this.editicon.setStyle('margin-left', '40px');
17616             }
17617             this.editicon.on('click', function(e) {
17618                 
17619                 // we fire even  if inothing is selected..
17620                 this.fireEvent('edit', this, this.lastData );
17621                 
17622             }, this);
17623         }
17624         */
17625         
17626         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17627             "up" : function(e){
17628                 this.inKeyMode = true;
17629                 this.selectPrev();
17630             },
17631
17632             "down" : function(e){
17633                 if(!this.isExpanded()){
17634                     this.onTriggerClick();
17635                 }else{
17636                     this.inKeyMode = true;
17637                     this.selectNext();
17638                 }
17639             },
17640
17641             "enter" : function(e){
17642 //                this.onViewClick();
17643                 //return true;
17644                 this.collapse();
17645                 
17646                 if(this.fireEvent("specialkey", this, e)){
17647                     this.onViewClick(false);
17648                 }
17649                 
17650                 return true;
17651             },
17652
17653             "esc" : function(e){
17654                 this.collapse();
17655             },
17656
17657             "tab" : function(e){
17658                 this.collapse();
17659                 
17660                 if(this.fireEvent("specialkey", this, e)){
17661                     this.onViewClick(false);
17662                 }
17663                 
17664                 return true;
17665             },
17666
17667             scope : this,
17668
17669             doRelay : function(foo, bar, hname){
17670                 if(hname == 'down' || this.scope.isExpanded()){
17671                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17672                 }
17673                 return true;
17674             },
17675
17676             forceKeyDown: true
17677         });
17678         
17679         
17680         this.queryDelay = Math.max(this.queryDelay || 10,
17681                 this.mode == 'local' ? 10 : 250);
17682         
17683         
17684         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17685         
17686         if(this.typeAhead){
17687             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17688         }
17689         if(this.editable !== false){
17690             this.inputEl().on("keyup", this.onKeyUp, this);
17691         }
17692         if(this.forceSelection){
17693             this.inputEl().on('blur', this.doForce, this);
17694         }
17695         
17696         if(this.multiple){
17697             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17698             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17699         }
17700     },
17701     
17702     initTickableEvents: function()
17703     {   
17704         this.createList();
17705         
17706         if(this.hiddenName){
17707             
17708             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17709             
17710             this.hiddenField.dom.value =
17711                 this.hiddenValue !== undefined ? this.hiddenValue :
17712                 this.value !== undefined ? this.value : '';
17713
17714             // prevent input submission
17715             this.el.dom.removeAttribute('name');
17716             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17717              
17718              
17719         }
17720         
17721 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17722         
17723         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17724         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17725         if(this.triggerList){
17726             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17727         }
17728          
17729         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17730         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17731         
17732         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17733         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17734         
17735         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17736         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17737         
17738         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17739         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17740         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17741         
17742         this.okBtn.hide();
17743         this.cancelBtn.hide();
17744         
17745         var _this = this;
17746         
17747         (function(){
17748             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17749             _this.list.setWidth(lw);
17750         }).defer(100);
17751         
17752         this.list.on('mouseover', this.onViewOver, this);
17753         this.list.on('mousemove', this.onViewMove, this);
17754         
17755         this.list.on('scroll', this.onViewScroll, this);
17756         
17757         if(!this.tpl){
17758             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17759                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17760         }
17761
17762         this.view = new Roo.View(this.list, this.tpl, {
17763             singleSelect:true,
17764             tickable:true,
17765             parent:this,
17766             store: this.store,
17767             selectedClass: this.selectedClass
17768         });
17769         
17770         //this.view.wrapEl.setDisplayed(false);
17771         this.view.on('click', this.onViewClick, this);
17772         
17773         
17774         
17775         this.store.on('beforeload', this.onBeforeLoad, this);
17776         this.store.on('load', this.onLoad, this);
17777         this.store.on('loadexception', this.onLoadException, this);
17778         
17779         if(this.editable){
17780             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17781                 "up" : function(e){
17782                     this.inKeyMode = true;
17783                     this.selectPrev();
17784                 },
17785
17786                 "down" : function(e){
17787                     this.inKeyMode = true;
17788                     this.selectNext();
17789                 },
17790
17791                 "enter" : function(e){
17792                     if(this.fireEvent("specialkey", this, e)){
17793                         this.onViewClick(false);
17794                     }
17795                     
17796                     return true;
17797                 },
17798
17799                 "esc" : function(e){
17800                     this.onTickableFooterButtonClick(e, false, false);
17801                 },
17802
17803                 "tab" : function(e){
17804                     this.fireEvent("specialkey", this, e);
17805                     
17806                     this.onTickableFooterButtonClick(e, false, false);
17807                     
17808                     return true;
17809                 },
17810
17811                 scope : this,
17812
17813                 doRelay : function(e, fn, key){
17814                     if(this.scope.isExpanded()){
17815                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17816                     }
17817                     return true;
17818                 },
17819
17820                 forceKeyDown: true
17821             });
17822         }
17823         
17824         this.queryDelay = Math.max(this.queryDelay || 10,
17825                 this.mode == 'local' ? 10 : 250);
17826         
17827         
17828         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17829         
17830         if(this.typeAhead){
17831             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17832         }
17833         
17834         if(this.editable !== false){
17835             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17836         }
17837         
17838         this.indicator = this.indicatorEl();
17839         
17840         if(this.indicator){
17841             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17842             this.indicator.hide();
17843         }
17844         
17845     },
17846
17847     onDestroy : function(){
17848         if(this.view){
17849             this.view.setStore(null);
17850             this.view.el.removeAllListeners();
17851             this.view.el.remove();
17852             this.view.purgeListeners();
17853         }
17854         if(this.list){
17855             this.list.dom.innerHTML  = '';
17856         }
17857         
17858         if(this.store){
17859             this.store.un('beforeload', this.onBeforeLoad, this);
17860             this.store.un('load', this.onLoad, this);
17861             this.store.un('loadexception', this.onLoadException, this);
17862         }
17863         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17864     },
17865
17866     // private
17867     fireKey : function(e){
17868         if(e.isNavKeyPress() && !this.list.isVisible()){
17869             this.fireEvent("specialkey", this, e);
17870         }
17871     },
17872
17873     // private
17874     onResize: function(w, h)
17875     {
17876         
17877         
17878 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17879 //        
17880 //        if(typeof w != 'number'){
17881 //            // we do not handle it!?!?
17882 //            return;
17883 //        }
17884 //        var tw = this.trigger.getWidth();
17885 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17886 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17887 //        var x = w - tw;
17888 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17889 //            
17890 //        //this.trigger.setStyle('left', x+'px');
17891 //        
17892 //        if(this.list && this.listWidth === undefined){
17893 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17894 //            this.list.setWidth(lw);
17895 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17896 //        }
17897         
17898     
17899         
17900     },
17901
17902     /**
17903      * Allow or prevent the user from directly editing the field text.  If false is passed,
17904      * the user will only be able to select from the items defined in the dropdown list.  This method
17905      * is the runtime equivalent of setting the 'editable' config option at config time.
17906      * @param {Boolean} value True to allow the user to directly edit the field text
17907      */
17908     setEditable : function(value){
17909         if(value == this.editable){
17910             return;
17911         }
17912         this.editable = value;
17913         if(!value){
17914             this.inputEl().dom.setAttribute('readOnly', true);
17915             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17916             this.inputEl().addClass('x-combo-noedit');
17917         }else{
17918             this.inputEl().dom.removeAttribute('readOnly');
17919             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17920             this.inputEl().removeClass('x-combo-noedit');
17921         }
17922     },
17923
17924     // private
17925     
17926     onBeforeLoad : function(combo,opts){
17927         if(!this.hasFocus){
17928             return;
17929         }
17930          if (!opts.add) {
17931             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17932          }
17933         this.restrictHeight();
17934         this.selectedIndex = -1;
17935     },
17936
17937     // private
17938     onLoad : function(){
17939         
17940         this.hasQuery = false;
17941         
17942         if(!this.hasFocus){
17943             return;
17944         }
17945         
17946         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17947             this.loading.hide();
17948         }
17949         
17950         if(this.store.getCount() > 0){
17951             
17952             this.expand();
17953             this.restrictHeight();
17954             if(this.lastQuery == this.allQuery){
17955                 if(this.editable && !this.tickable){
17956                     this.inputEl().dom.select();
17957                 }
17958                 
17959                 if(
17960                     !this.selectByValue(this.value, true) &&
17961                     this.autoFocus && 
17962                     (
17963                         !this.store.lastOptions ||
17964                         typeof(this.store.lastOptions.add) == 'undefined' || 
17965                         this.store.lastOptions.add != true
17966                     )
17967                 ){
17968                     this.select(0, true);
17969                 }
17970             }else{
17971                 if(this.autoFocus){
17972                     this.selectNext();
17973                 }
17974                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17975                     this.taTask.delay(this.typeAheadDelay);
17976                 }
17977             }
17978         }else{
17979             this.onEmptyResults();
17980         }
17981         
17982         //this.el.focus();
17983     },
17984     // private
17985     onLoadException : function()
17986     {
17987         this.hasQuery = false;
17988         
17989         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17990             this.loading.hide();
17991         }
17992         
17993         if(this.tickable && this.editable){
17994             return;
17995         }
17996         
17997         this.collapse();
17998         // only causes errors at present
17999         //Roo.log(this.store.reader.jsonData);
18000         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18001             // fixme
18002             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18003         //}
18004         
18005         
18006     },
18007     // private
18008     onTypeAhead : function(){
18009         if(this.store.getCount() > 0){
18010             var r = this.store.getAt(0);
18011             var newValue = r.data[this.displayField];
18012             var len = newValue.length;
18013             var selStart = this.getRawValue().length;
18014             
18015             if(selStart != len){
18016                 this.setRawValue(newValue);
18017                 this.selectText(selStart, newValue.length);
18018             }
18019         }
18020     },
18021
18022     // private
18023     onSelect : function(record, index){
18024         
18025         if(this.fireEvent('beforeselect', this, record, index) !== false){
18026         
18027             this.setFromData(index > -1 ? record.data : false);
18028             
18029             this.collapse();
18030             this.fireEvent('select', this, record, index);
18031         }
18032     },
18033
18034     /**
18035      * Returns the currently selected field value or empty string if no value is set.
18036      * @return {String} value The selected value
18037      */
18038     getValue : function()
18039     {
18040         if(Roo.isIOS && this.useNativeIOS){
18041             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18042         }
18043         
18044         if(this.multiple){
18045             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18046         }
18047         
18048         if(this.valueField){
18049             return typeof this.value != 'undefined' ? this.value : '';
18050         }else{
18051             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
18052         }
18053     },
18054     
18055     getRawValue : function()
18056     {
18057         if(Roo.isIOS && this.useNativeIOS){
18058             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18059         }
18060         
18061         var v = this.inputEl().getValue();
18062         
18063         return v;
18064     },
18065
18066     /**
18067      * Clears any text/value currently set in the field
18068      */
18069     clearValue : function(){
18070         
18071         if(this.hiddenField){
18072             this.hiddenField.dom.value = '';
18073         }
18074         this.value = '';
18075         this.setRawValue('');
18076         this.lastSelectionText = '';
18077         this.lastData = false;
18078         
18079         var close = this.closeTriggerEl();
18080         
18081         if(close){
18082             close.hide();
18083         }
18084         
18085         this.validate();
18086         
18087     },
18088
18089     /**
18090      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18091      * will be displayed in the field.  If the value does not match the data value of an existing item,
18092      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18093      * Otherwise the field will be blank (although the value will still be set).
18094      * @param {String} value The value to match
18095      */
18096     setValue : function(v)
18097     {
18098         if(Roo.isIOS && this.useNativeIOS){
18099             this.setIOSValue(v);
18100             return;
18101         }
18102         
18103         if(this.multiple){
18104             this.syncValue();
18105             return;
18106         }
18107         
18108         var text = v;
18109         if(this.valueField){
18110             var r = this.findRecord(this.valueField, v);
18111             if(r){
18112                 text = r.data[this.displayField];
18113             }else if(this.valueNotFoundText !== undefined){
18114                 text = this.valueNotFoundText;
18115             }
18116         }
18117         this.lastSelectionText = text;
18118         if(this.hiddenField){
18119             this.hiddenField.dom.value = v;
18120         }
18121         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
18122         this.value = v;
18123         
18124         var close = this.closeTriggerEl();
18125         
18126         if(close){
18127             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18128         }
18129         
18130         this.validate();
18131     },
18132     /**
18133      * @property {Object} the last set data for the element
18134      */
18135     
18136     lastData : false,
18137     /**
18138      * Sets the value of the field based on a object which is related to the record format for the store.
18139      * @param {Object} value the value to set as. or false on reset?
18140      */
18141     setFromData : function(o){
18142         
18143         if(this.multiple){
18144             this.addItem(o);
18145             return;
18146         }
18147             
18148         var dv = ''; // display value
18149         var vv = ''; // value value..
18150         this.lastData = o;
18151         if (this.displayField) {
18152             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18153         } else {
18154             // this is an error condition!!!
18155             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18156         }
18157         
18158         if(this.valueField){
18159             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18160         }
18161         
18162         var close = this.closeTriggerEl();
18163         
18164         if(close){
18165             if(dv.length || vv * 1 > 0){
18166                 close.show() ;
18167                 this.blockFocus=true;
18168             } else {
18169                 close.hide();
18170             }             
18171         }
18172         
18173         if(this.hiddenField){
18174             this.hiddenField.dom.value = vv;
18175             
18176             this.lastSelectionText = dv;
18177             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
18178             this.value = vv;
18179             return;
18180         }
18181         // no hidden field.. - we store the value in 'value', but still display
18182         // display field!!!!
18183         this.lastSelectionText = dv;
18184         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
18185         this.value = vv;
18186         
18187         
18188         
18189     },
18190     // private
18191     reset : function(){
18192         // overridden so that last data is reset..
18193         
18194         if(this.multiple){
18195             this.clearItem();
18196             return;
18197         }
18198         
18199         this.setValue(this.originalValue);
18200         //this.clearInvalid();
18201         this.lastData = false;
18202         if (this.view) {
18203             this.view.clearSelections();
18204         }
18205         
18206         this.validate();
18207     },
18208     // private
18209     findRecord : function(prop, value){
18210         var record;
18211         if(this.store.getCount() > 0){
18212             this.store.each(function(r){
18213                 if(r.data[prop] == value){
18214                     record = r;
18215                     return false;
18216                 }
18217                 return true;
18218             });
18219         }
18220         return record;
18221     },
18222     
18223     getName: function()
18224     {
18225         // returns hidden if it's set..
18226         if (!this.rendered) {return ''};
18227         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18228         
18229     },
18230     // private
18231     onViewMove : function(e, t){
18232         this.inKeyMode = false;
18233     },
18234
18235     // private
18236     onViewOver : function(e, t){
18237         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18238             return;
18239         }
18240         var item = this.view.findItemFromChild(t);
18241         
18242         if(item){
18243             var index = this.view.indexOf(item);
18244             this.select(index, false);
18245         }
18246     },
18247
18248     // private
18249     onViewClick : function(view, doFocus, el, e)
18250     {
18251         var index = this.view.getSelectedIndexes()[0];
18252         
18253         var r = this.store.getAt(index);
18254         
18255         if(this.tickable){
18256             
18257             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18258                 return;
18259             }
18260             
18261             var rm = false;
18262             var _this = this;
18263             
18264             Roo.each(this.tickItems, function(v,k){
18265                 
18266                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18267                     Roo.log(v);
18268                     _this.tickItems.splice(k, 1);
18269                     
18270                     if(typeof(e) == 'undefined' && view == false){
18271                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18272                     }
18273                     
18274                     rm = true;
18275                     return;
18276                 }
18277             });
18278             
18279             if(rm){
18280                 return;
18281             }
18282             
18283             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18284                 this.tickItems.push(r.data);
18285             }
18286             
18287             if(typeof(e) == 'undefined' && view == false){
18288                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18289             }
18290                     
18291             return;
18292         }
18293         
18294         if(r){
18295             this.onSelect(r, index);
18296         }
18297         if(doFocus !== false && !this.blockFocus){
18298             this.inputEl().focus();
18299         }
18300     },
18301
18302     // private
18303     restrictHeight : function(){
18304         //this.innerList.dom.style.height = '';
18305         //var inner = this.innerList.dom;
18306         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18307         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18308         //this.list.beginUpdate();
18309         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18310         this.list.alignTo(this.inputEl(), this.listAlign);
18311         this.list.alignTo(this.inputEl(), this.listAlign);
18312         //this.list.endUpdate();
18313     },
18314
18315     // private
18316     onEmptyResults : function(){
18317         
18318         if(this.tickable && this.editable){
18319             this.hasFocus = false;
18320             this.restrictHeight();
18321             return;
18322         }
18323         
18324         this.collapse();
18325     },
18326
18327     /**
18328      * Returns true if the dropdown list is expanded, else false.
18329      */
18330     isExpanded : function(){
18331         return this.list.isVisible();
18332     },
18333
18334     /**
18335      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18336      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18337      * @param {String} value The data value of the item to select
18338      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18339      * selected item if it is not currently in view (defaults to true)
18340      * @return {Boolean} True if the value matched an item in the list, else false
18341      */
18342     selectByValue : function(v, scrollIntoView){
18343         if(v !== undefined && v !== null){
18344             var r = this.findRecord(this.valueField || this.displayField, v);
18345             if(r){
18346                 this.select(this.store.indexOf(r), scrollIntoView);
18347                 return true;
18348             }
18349         }
18350         return false;
18351     },
18352
18353     /**
18354      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18355      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18356      * @param {Number} index The zero-based index of the list item to select
18357      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18358      * selected item if it is not currently in view (defaults to true)
18359      */
18360     select : function(index, scrollIntoView){
18361         this.selectedIndex = index;
18362         this.view.select(index);
18363         if(scrollIntoView !== false){
18364             var el = this.view.getNode(index);
18365             /*
18366              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18367              */
18368             if(el){
18369                 this.list.scrollChildIntoView(el, false);
18370             }
18371         }
18372     },
18373
18374     // private
18375     selectNext : function(){
18376         var ct = this.store.getCount();
18377         if(ct > 0){
18378             if(this.selectedIndex == -1){
18379                 this.select(0);
18380             }else if(this.selectedIndex < ct-1){
18381                 this.select(this.selectedIndex+1);
18382             }
18383         }
18384     },
18385
18386     // private
18387     selectPrev : function(){
18388         var ct = this.store.getCount();
18389         if(ct > 0){
18390             if(this.selectedIndex == -1){
18391                 this.select(0);
18392             }else if(this.selectedIndex != 0){
18393                 this.select(this.selectedIndex-1);
18394             }
18395         }
18396     },
18397
18398     // private
18399     onKeyUp : function(e){
18400         if(this.editable !== false && !e.isSpecialKey()){
18401             this.lastKey = e.getKey();
18402             this.dqTask.delay(this.queryDelay);
18403         }
18404     },
18405
18406     // private
18407     validateBlur : function(){
18408         return !this.list || !this.list.isVisible();   
18409     },
18410
18411     // private
18412     initQuery : function(){
18413         
18414         var v = this.getRawValue();
18415         
18416         if(this.tickable && this.editable){
18417             v = this.tickableInputEl().getValue();
18418         }
18419         
18420         this.doQuery(v);
18421     },
18422
18423     // private
18424     doForce : function(){
18425         if(this.inputEl().dom.value.length > 0){
18426             this.inputEl().dom.value =
18427                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18428              
18429         }
18430     },
18431
18432     /**
18433      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18434      * query allowing the query action to be canceled if needed.
18435      * @param {String} query The SQL query to execute
18436      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18437      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18438      * saved in the current store (defaults to false)
18439      */
18440     doQuery : function(q, forceAll){
18441         
18442         if(q === undefined || q === null){
18443             q = '';
18444         }
18445         var qe = {
18446             query: q,
18447             forceAll: forceAll,
18448             combo: this,
18449             cancel:false
18450         };
18451         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18452             return false;
18453         }
18454         q = qe.query;
18455         
18456         forceAll = qe.forceAll;
18457         if(forceAll === true || (q.length >= this.minChars)){
18458             
18459             this.hasQuery = true;
18460             
18461             if(this.lastQuery != q || this.alwaysQuery){
18462                 this.lastQuery = q;
18463                 if(this.mode == 'local'){
18464                     this.selectedIndex = -1;
18465                     if(forceAll){
18466                         this.store.clearFilter();
18467                     }else{
18468                         
18469                         if(this.specialFilter){
18470                             this.fireEvent('specialfilter', this);
18471                             this.onLoad();
18472                             return;
18473                         }
18474                         
18475                         this.store.filter(this.displayField, q);
18476                     }
18477                     
18478                     this.store.fireEvent("datachanged", this.store);
18479                     
18480                     this.onLoad();
18481                     
18482                     
18483                 }else{
18484                     
18485                     this.store.baseParams[this.queryParam] = q;
18486                     
18487                     var options = {params : this.getParams(q)};
18488                     
18489                     if(this.loadNext){
18490                         options.add = true;
18491                         options.params.start = this.page * this.pageSize;
18492                     }
18493                     
18494                     this.store.load(options);
18495                     
18496                     /*
18497                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18498                      *  we should expand the list on onLoad
18499                      *  so command out it
18500                      */
18501 //                    this.expand();
18502                 }
18503             }else{
18504                 this.selectedIndex = -1;
18505                 this.onLoad();   
18506             }
18507         }
18508         
18509         this.loadNext = false;
18510     },
18511     
18512     // private
18513     getParams : function(q){
18514         var p = {};
18515         //p[this.queryParam] = q;
18516         
18517         if(this.pageSize){
18518             p.start = 0;
18519             p.limit = this.pageSize;
18520         }
18521         return p;
18522     },
18523
18524     /**
18525      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18526      */
18527     collapse : function(){
18528         if(!this.isExpanded()){
18529             return;
18530         }
18531         
18532         this.list.hide();
18533         
18534         this.hasFocus = false;
18535         
18536         if(this.tickable){
18537             this.okBtn.hide();
18538             this.cancelBtn.hide();
18539             this.trigger.show();
18540             
18541             if(this.editable){
18542                 this.tickableInputEl().dom.value = '';
18543                 this.tickableInputEl().blur();
18544             }
18545             
18546         }
18547         
18548         Roo.get(document).un('mousedown', this.collapseIf, this);
18549         Roo.get(document).un('mousewheel', this.collapseIf, this);
18550         if (!this.editable) {
18551             Roo.get(document).un('keydown', this.listKeyPress, this);
18552         }
18553         this.fireEvent('collapse', this);
18554         
18555         this.validate();
18556     },
18557
18558     // private
18559     collapseIf : function(e){
18560         var in_combo  = e.within(this.el);
18561         var in_list =  e.within(this.list);
18562         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18563         
18564         if (in_combo || in_list || is_list) {
18565             //e.stopPropagation();
18566             return;
18567         }
18568         
18569         if(this.tickable){
18570             this.onTickableFooterButtonClick(e, false, false);
18571         }
18572
18573         this.collapse();
18574         
18575     },
18576
18577     /**
18578      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18579      */
18580     expand : function(){
18581        
18582         if(this.isExpanded() || !this.hasFocus){
18583             return;
18584         }
18585         
18586         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18587         this.list.setWidth(lw);
18588         
18589         Roo.log('expand');
18590         
18591         this.list.show();
18592         
18593         this.restrictHeight();
18594         
18595         if(this.tickable){
18596             
18597             this.tickItems = Roo.apply([], this.item);
18598             
18599             this.okBtn.show();
18600             this.cancelBtn.show();
18601             this.trigger.hide();
18602             
18603             if(this.editable){
18604                 this.tickableInputEl().focus();
18605             }
18606             
18607         }
18608         
18609         Roo.get(document).on('mousedown', this.collapseIf, this);
18610         Roo.get(document).on('mousewheel', this.collapseIf, this);
18611         if (!this.editable) {
18612             Roo.get(document).on('keydown', this.listKeyPress, this);
18613         }
18614         
18615         this.fireEvent('expand', this);
18616     },
18617
18618     // private
18619     // Implements the default empty TriggerField.onTriggerClick function
18620     onTriggerClick : function(e)
18621     {
18622         Roo.log('trigger click');
18623         
18624         if(this.disabled || !this.triggerList){
18625             return;
18626         }
18627         
18628         this.page = 0;
18629         this.loadNext = false;
18630         
18631         if(this.isExpanded()){
18632             this.collapse();
18633             if (!this.blockFocus) {
18634                 this.inputEl().focus();
18635             }
18636             
18637         }else {
18638             this.hasFocus = true;
18639             if(this.triggerAction == 'all') {
18640                 this.doQuery(this.allQuery, true);
18641             } else {
18642                 this.doQuery(this.getRawValue());
18643             }
18644             if (!this.blockFocus) {
18645                 this.inputEl().focus();
18646             }
18647         }
18648     },
18649     
18650     onTickableTriggerClick : function(e)
18651     {
18652         if(this.disabled){
18653             return;
18654         }
18655         
18656         this.page = 0;
18657         this.loadNext = false;
18658         this.hasFocus = true;
18659         
18660         if(this.triggerAction == 'all') {
18661             this.doQuery(this.allQuery, true);
18662         } else {
18663             this.doQuery(this.getRawValue());
18664         }
18665     },
18666     
18667     onSearchFieldClick : function(e)
18668     {
18669         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18670             this.onTickableFooterButtonClick(e, false, false);
18671             return;
18672         }
18673         
18674         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18675             return;
18676         }
18677         
18678         this.page = 0;
18679         this.loadNext = false;
18680         this.hasFocus = true;
18681         
18682         if(this.triggerAction == 'all') {
18683             this.doQuery(this.allQuery, true);
18684         } else {
18685             this.doQuery(this.getRawValue());
18686         }
18687     },
18688     
18689     listKeyPress : function(e)
18690     {
18691         //Roo.log('listkeypress');
18692         // scroll to first matching element based on key pres..
18693         if (e.isSpecialKey()) {
18694             return false;
18695         }
18696         var k = String.fromCharCode(e.getKey()).toUpperCase();
18697         //Roo.log(k);
18698         var match  = false;
18699         var csel = this.view.getSelectedNodes();
18700         var cselitem = false;
18701         if (csel.length) {
18702             var ix = this.view.indexOf(csel[0]);
18703             cselitem  = this.store.getAt(ix);
18704             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18705                 cselitem = false;
18706             }
18707             
18708         }
18709         
18710         this.store.each(function(v) { 
18711             if (cselitem) {
18712                 // start at existing selection.
18713                 if (cselitem.id == v.id) {
18714                     cselitem = false;
18715                 }
18716                 return true;
18717             }
18718                 
18719             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18720                 match = this.store.indexOf(v);
18721                 return false;
18722             }
18723             return true;
18724         }, this);
18725         
18726         if (match === false) {
18727             return true; // no more action?
18728         }
18729         // scroll to?
18730         this.view.select(match);
18731         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18732         sn.scrollIntoView(sn.dom.parentNode, false);
18733     },
18734     
18735     onViewScroll : function(e, t){
18736         
18737         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){
18738             return;
18739         }
18740         
18741         this.hasQuery = true;
18742         
18743         this.loading = this.list.select('.loading', true).first();
18744         
18745         if(this.loading === null){
18746             this.list.createChild({
18747                 tag: 'div',
18748                 cls: 'loading roo-select2-more-results roo-select2-active',
18749                 html: 'Loading more results...'
18750             });
18751             
18752             this.loading = this.list.select('.loading', true).first();
18753             
18754             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18755             
18756             this.loading.hide();
18757         }
18758         
18759         this.loading.show();
18760         
18761         var _combo = this;
18762         
18763         this.page++;
18764         this.loadNext = true;
18765         
18766         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18767         
18768         return;
18769     },
18770     
18771     addItem : function(o)
18772     {   
18773         var dv = ''; // display value
18774         
18775         if (this.displayField) {
18776             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18777         } else {
18778             // this is an error condition!!!
18779             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18780         }
18781         
18782         if(!dv.length){
18783             return;
18784         }
18785         
18786         var choice = this.choices.createChild({
18787             tag: 'li',
18788             cls: 'roo-select2-search-choice',
18789             cn: [
18790                 {
18791                     tag: 'div',
18792                     html: dv
18793                 },
18794                 {
18795                     tag: 'a',
18796                     href: '#',
18797                     cls: 'roo-select2-search-choice-close fa fa-times',
18798                     tabindex: '-1'
18799                 }
18800             ]
18801             
18802         }, this.searchField);
18803         
18804         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18805         
18806         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18807         
18808         this.item.push(o);
18809         
18810         this.lastData = o;
18811         
18812         this.syncValue();
18813         
18814         this.inputEl().dom.value = '';
18815         
18816         this.validate();
18817     },
18818     
18819     onRemoveItem : function(e, _self, o)
18820     {
18821         e.preventDefault();
18822         
18823         this.lastItem = Roo.apply([], this.item);
18824         
18825         var index = this.item.indexOf(o.data) * 1;
18826         
18827         if( index < 0){
18828             Roo.log('not this item?!');
18829             return;
18830         }
18831         
18832         this.item.splice(index, 1);
18833         o.item.remove();
18834         
18835         this.syncValue();
18836         
18837         this.fireEvent('remove', this, e);
18838         
18839         this.validate();
18840         
18841     },
18842     
18843     syncValue : function()
18844     {
18845         if(!this.item.length){
18846             this.clearValue();
18847             return;
18848         }
18849             
18850         var value = [];
18851         var _this = this;
18852         Roo.each(this.item, function(i){
18853             if(_this.valueField){
18854                 value.push(i[_this.valueField]);
18855                 return;
18856             }
18857
18858             value.push(i);
18859         });
18860
18861         this.value = value.join(',');
18862
18863         if(this.hiddenField){
18864             this.hiddenField.dom.value = this.value;
18865         }
18866         
18867         this.store.fireEvent("datachanged", this.store);
18868         
18869         this.validate();
18870     },
18871     
18872     clearItem : function()
18873     {
18874         if(!this.multiple){
18875             return;
18876         }
18877         
18878         this.item = [];
18879         
18880         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18881            c.remove();
18882         });
18883         
18884         this.syncValue();
18885         
18886         this.validate();
18887         
18888         if(this.tickable && !Roo.isTouch){
18889             this.view.refresh();
18890         }
18891     },
18892     
18893     inputEl: function ()
18894     {
18895         if(Roo.isIOS && this.useNativeIOS){
18896             return this.el.select('select.roo-ios-select', true).first();
18897         }
18898         
18899         if(Roo.isTouch && this.mobileTouchView){
18900             return this.el.select('input.form-control',true).first();
18901         }
18902         
18903         if(this.tickable){
18904             return this.searchField;
18905         }
18906         
18907         return this.el.select('input.form-control',true).first();
18908     },
18909     
18910     onTickableFooterButtonClick : function(e, btn, el)
18911     {
18912         e.preventDefault();
18913         
18914         this.lastItem = Roo.apply([], this.item);
18915         
18916         if(btn && btn.name == 'cancel'){
18917             this.tickItems = Roo.apply([], this.item);
18918             this.collapse();
18919             return;
18920         }
18921         
18922         this.clearItem();
18923         
18924         var _this = this;
18925         
18926         Roo.each(this.tickItems, function(o){
18927             _this.addItem(o);
18928         });
18929         
18930         this.collapse();
18931         
18932     },
18933     
18934     validate : function()
18935     {
18936         if(this.getVisibilityEl().hasClass('hidden')){
18937             return true;
18938         }
18939         
18940         var v = this.getRawValue();
18941         
18942         if(this.multiple){
18943             v = this.getValue();
18944         }
18945         
18946         if(this.disabled || this.allowBlank || v.length){
18947             this.markValid();
18948             return true;
18949         }
18950         
18951         this.markInvalid();
18952         return false;
18953     },
18954     
18955     tickableInputEl : function()
18956     {
18957         if(!this.tickable || !this.editable){
18958             return this.inputEl();
18959         }
18960         
18961         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18962     },
18963     
18964     
18965     getAutoCreateTouchView : function()
18966     {
18967         var id = Roo.id();
18968         
18969         var cfg = {
18970             cls: 'form-group' //input-group
18971         };
18972         
18973         var input =  {
18974             tag: 'input',
18975             id : id,
18976             type : this.inputType,
18977             cls : 'form-control x-combo-noedit',
18978             autocomplete: 'new-password',
18979             placeholder : this.placeholder || '',
18980             readonly : true
18981         };
18982         
18983         if (this.name) {
18984             input.name = this.name;
18985         }
18986         
18987         if (this.size) {
18988             input.cls += ' input-' + this.size;
18989         }
18990         
18991         if (this.disabled) {
18992             input.disabled = true;
18993         }
18994         
18995         var inputblock = {
18996             cls : 'roo-combobox-wrap',
18997             cn : [
18998                 input
18999             ]
19000         };
19001         
19002         if(this.before){
19003             inputblock.cls += ' input-group';
19004             
19005             inputblock.cn.unshift({
19006                 tag :'span',
19007                 cls : 'input-group-addon input-group-prepend input-group-text',
19008                 html : this.before
19009             });
19010         }
19011         
19012         if(this.removable && !this.multiple){
19013             inputblock.cls += ' roo-removable';
19014             
19015             inputblock.cn.push({
19016                 tag: 'button',
19017                 html : 'x',
19018                 cls : 'roo-combo-removable-btn close'
19019             });
19020         }
19021
19022         if(this.hasFeedback && !this.allowBlank){
19023             
19024             inputblock.cls += ' has-feedback';
19025             
19026             inputblock.cn.push({
19027                 tag: 'span',
19028                 cls: 'glyphicon form-control-feedback'
19029             });
19030             
19031         }
19032         
19033         if (this.after) {
19034             
19035             inputblock.cls += (this.before) ? '' : ' input-group';
19036             
19037             inputblock.cn.push({
19038                 tag :'span',
19039                 cls : 'input-group-addon input-group-append input-group-text',
19040                 html : this.after
19041             });
19042         }
19043
19044         
19045         var ibwrap = inputblock;
19046         
19047         if(this.multiple){
19048             ibwrap = {
19049                 tag: 'ul',
19050                 cls: 'roo-select2-choices',
19051                 cn:[
19052                     {
19053                         tag: 'li',
19054                         cls: 'roo-select2-search-field',
19055                         cn: [
19056
19057                             inputblock
19058                         ]
19059                     }
19060                 ]
19061             };
19062         
19063             
19064         }
19065         
19066         var combobox = {
19067             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19068             cn: [
19069                 {
19070                     tag: 'input',
19071                     type : 'hidden',
19072                     cls: 'form-hidden-field'
19073                 },
19074                 ibwrap
19075             ]
19076         };
19077         
19078         if(!this.multiple && this.showToggleBtn){
19079             
19080             var caret = {
19081                 cls: 'caret'
19082             };
19083             
19084             if (this.caret != false) {
19085                 caret = {
19086                      tag: 'i',
19087                      cls: 'fa fa-' + this.caret
19088                 };
19089                 
19090             }
19091             
19092             combobox.cn.push({
19093                 tag :'span',
19094                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19095                 cn : [
19096                     Roo.bootstrap.version == 3 ? caret : '',
19097                     {
19098                         tag: 'span',
19099                         cls: 'combobox-clear',
19100                         cn  : [
19101                             {
19102                                 tag : 'i',
19103                                 cls: 'icon-remove'
19104                             }
19105                         ]
19106                     }
19107                 ]
19108
19109             })
19110         }
19111         
19112         if(this.multiple){
19113             combobox.cls += ' roo-select2-container-multi';
19114         }
19115         
19116         var required =  this.allowBlank ?  {
19117                     tag : 'i',
19118                     style: 'display: none'
19119                 } : {
19120                    tag : 'i',
19121                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19122                    tooltip : 'This field is required'
19123                 };
19124         
19125         var align = this.labelAlign || this.parentLabelAlign();
19126         
19127         if (align ==='left' && this.fieldLabel.length) {
19128
19129             cfg.cn = [
19130                 required,
19131                 {
19132                     tag: 'label',
19133                     cls : 'control-label col-form-label',
19134                     html : this.fieldLabel
19135
19136                 },
19137                 {
19138                     cls : 'roo-combobox-wrap ', 
19139                     cn: [
19140                         combobox
19141                     ]
19142                 }
19143             ];
19144             
19145             var labelCfg = cfg.cn[1];
19146             var contentCfg = cfg.cn[2];
19147             
19148
19149             if(this.indicatorpos == 'right'){
19150                 cfg.cn = [
19151                     {
19152                         tag: 'label',
19153                         'for' :  id,
19154                         cls : 'control-label col-form-label',
19155                         cn : [
19156                             {
19157                                 tag : 'span',
19158                                 html : this.fieldLabel
19159                             },
19160                             required
19161                         ]
19162                     },
19163                     {
19164                         cls : "roo-combobox-wrap ",
19165                         cn: [
19166                             combobox
19167                         ]
19168                     }
19169
19170                 ];
19171                 
19172                 labelCfg = cfg.cn[0];
19173                 contentCfg = cfg.cn[1];
19174             }
19175             
19176            
19177             
19178             if(this.labelWidth > 12){
19179                 labelCfg.style = "width: " + this.labelWidth + 'px';
19180             }
19181            
19182             if(this.labelWidth < 13 && this.labelmd == 0){
19183                 this.labelmd = this.labelWidth;
19184             }
19185             
19186             if(this.labellg > 0){
19187                 labelCfg.cls += ' col-lg-' + this.labellg;
19188                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19189             }
19190             
19191             if(this.labelmd > 0){
19192                 labelCfg.cls += ' col-md-' + this.labelmd;
19193                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19194             }
19195             
19196             if(this.labelsm > 0){
19197                 labelCfg.cls += ' col-sm-' + this.labelsm;
19198                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19199             }
19200             
19201             if(this.labelxs > 0){
19202                 labelCfg.cls += ' col-xs-' + this.labelxs;
19203                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19204             }
19205                 
19206                 
19207         } else if ( this.fieldLabel.length) {
19208             cfg.cn = [
19209                required,
19210                 {
19211                     tag: 'label',
19212                     cls : 'control-label',
19213                     html : this.fieldLabel
19214
19215                 },
19216                 {
19217                     cls : '', 
19218                     cn: [
19219                         combobox
19220                     ]
19221                 }
19222             ];
19223             
19224             if(this.indicatorpos == 'right'){
19225                 cfg.cn = [
19226                     {
19227                         tag: 'label',
19228                         cls : 'control-label',
19229                         html : this.fieldLabel,
19230                         cn : [
19231                             required
19232                         ]
19233                     },
19234                     {
19235                         cls : '', 
19236                         cn: [
19237                             combobox
19238                         ]
19239                     }
19240                 ];
19241             }
19242         } else {
19243             cfg.cn = combobox;    
19244         }
19245         
19246         
19247         var settings = this;
19248         
19249         ['xs','sm','md','lg'].map(function(size){
19250             if (settings[size]) {
19251                 cfg.cls += ' col-' + size + '-' + settings[size];
19252             }
19253         });
19254         
19255         return cfg;
19256     },
19257     
19258     initTouchView : function()
19259     {
19260         this.renderTouchView();
19261         
19262         this.touchViewEl.on('scroll', function(){
19263             this.el.dom.scrollTop = 0;
19264         }, this);
19265         
19266         this.originalValue = this.getValue();
19267         
19268         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19269         
19270         this.inputEl().on("click", this.showTouchView, this);
19271         if (this.triggerEl) {
19272             this.triggerEl.on("click", this.showTouchView, this);
19273         }
19274         
19275         
19276         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19277         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19278         
19279         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19280         
19281         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19282         this.store.on('load', this.onTouchViewLoad, this);
19283         this.store.on('loadexception', this.onTouchViewLoadException, this);
19284         
19285         if(this.hiddenName){
19286             
19287             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19288             
19289             this.hiddenField.dom.value =
19290                 this.hiddenValue !== undefined ? this.hiddenValue :
19291                 this.value !== undefined ? this.value : '';
19292         
19293             this.el.dom.removeAttribute('name');
19294             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19295         }
19296         
19297         if(this.multiple){
19298             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19299             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19300         }
19301         
19302         if(this.removable && !this.multiple){
19303             var close = this.closeTriggerEl();
19304             if(close){
19305                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19306                 close.on('click', this.removeBtnClick, this, close);
19307             }
19308         }
19309         /*
19310          * fix the bug in Safari iOS8
19311          */
19312         this.inputEl().on("focus", function(e){
19313             document.activeElement.blur();
19314         }, this);
19315         
19316         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19317         
19318         return;
19319         
19320         
19321     },
19322     
19323     renderTouchView : function()
19324     {
19325         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
19326         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327         
19328         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19329         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19330         
19331         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19332         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19333         this.touchViewBodyEl.setStyle('overflow', 'auto');
19334         
19335         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19336         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19337         
19338         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19339         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19340         
19341     },
19342     
19343     showTouchView : function()
19344     {
19345         if(this.disabled){
19346             return;
19347         }
19348         
19349         this.touchViewHeaderEl.hide();
19350
19351         if(this.modalTitle.length){
19352             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19353             this.touchViewHeaderEl.show();
19354         }
19355
19356         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19357         this.touchViewEl.show();
19358
19359         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19360         
19361         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19362         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19363
19364         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19365
19366         if(this.modalTitle.length){
19367             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19368         }
19369         
19370         this.touchViewBodyEl.setHeight(bodyHeight);
19371
19372         if(this.animate){
19373             var _this = this;
19374             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19375         }else{
19376             this.touchViewEl.addClass(['in','show']);
19377         }
19378         
19379         if(this._touchViewMask){
19380             Roo.get(document.body).addClass("x-body-masked");
19381             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19382             this._touchViewMask.setStyle('z-index', 10000);
19383             this._touchViewMask.addClass('show');
19384         }
19385         
19386         this.doTouchViewQuery();
19387         
19388     },
19389     
19390     hideTouchView : function()
19391     {
19392         this.touchViewEl.removeClass(['in','show']);
19393
19394         if(this.animate){
19395             var _this = this;
19396             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19397         }else{
19398             this.touchViewEl.setStyle('display', 'none');
19399         }
19400         
19401         if(this._touchViewMask){
19402             this._touchViewMask.removeClass('show');
19403             Roo.get(document.body).removeClass("x-body-masked");
19404         }
19405     },
19406     
19407     setTouchViewValue : function()
19408     {
19409         if(this.multiple){
19410             this.clearItem();
19411         
19412             var _this = this;
19413
19414             Roo.each(this.tickItems, function(o){
19415                 this.addItem(o);
19416             }, this);
19417         }
19418         
19419         this.hideTouchView();
19420     },
19421     
19422     doTouchViewQuery : function()
19423     {
19424         var qe = {
19425             query: '',
19426             forceAll: true,
19427             combo: this,
19428             cancel:false
19429         };
19430         
19431         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19432             return false;
19433         }
19434         
19435         if(!this.alwaysQuery || this.mode == 'local'){
19436             this.onTouchViewLoad();
19437             return;
19438         }
19439         
19440         this.store.load();
19441     },
19442     
19443     onTouchViewBeforeLoad : function(combo,opts)
19444     {
19445         return;
19446     },
19447
19448     // private
19449     onTouchViewLoad : function()
19450     {
19451         if(this.store.getCount() < 1){
19452             this.onTouchViewEmptyResults();
19453             return;
19454         }
19455         
19456         this.clearTouchView();
19457         
19458         var rawValue = this.getRawValue();
19459         
19460         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19461         
19462         this.tickItems = [];
19463         
19464         this.store.data.each(function(d, rowIndex){
19465             var row = this.touchViewListGroup.createChild(template);
19466             
19467             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19468                 row.addClass(d.data.cls);
19469             }
19470             
19471             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19472                 var cfg = {
19473                     data : d.data,
19474                     html : d.data[this.displayField]
19475                 };
19476                 
19477                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19478                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19479                 }
19480             }
19481             row.removeClass('selected');
19482             if(!this.multiple && this.valueField &&
19483                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19484             {
19485                 // radio buttons..
19486                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19487                 row.addClass('selected');
19488             }
19489             
19490             if(this.multiple && this.valueField &&
19491                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19492             {
19493                 
19494                 // checkboxes...
19495                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19496                 this.tickItems.push(d.data);
19497             }
19498             
19499             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19500             
19501         }, this);
19502         
19503         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19504         
19505         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19506
19507         if(this.modalTitle.length){
19508             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19509         }
19510
19511         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19512         
19513         if(this.mobile_restrict_height && listHeight < bodyHeight){
19514             this.touchViewBodyEl.setHeight(listHeight);
19515         }
19516         
19517         var _this = this;
19518         
19519         if(firstChecked && listHeight > bodyHeight){
19520             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19521         }
19522         
19523     },
19524     
19525     onTouchViewLoadException : function()
19526     {
19527         this.hideTouchView();
19528     },
19529     
19530     onTouchViewEmptyResults : function()
19531     {
19532         this.clearTouchView();
19533         
19534         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19535         
19536         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19537         
19538     },
19539     
19540     clearTouchView : function()
19541     {
19542         this.touchViewListGroup.dom.innerHTML = '';
19543     },
19544     
19545     onTouchViewClick : function(e, el, o)
19546     {
19547         e.preventDefault();
19548         
19549         var row = o.row;
19550         var rowIndex = o.rowIndex;
19551         
19552         var r = this.store.getAt(rowIndex);
19553         
19554         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19555             
19556             if(!this.multiple){
19557                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19558                     c.dom.removeAttribute('checked');
19559                 }, this);
19560
19561                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19562
19563                 this.setFromData(r.data);
19564
19565                 var close = this.closeTriggerEl();
19566
19567                 if(close){
19568                     close.show();
19569                 }
19570
19571                 this.hideTouchView();
19572
19573                 this.fireEvent('select', this, r, rowIndex);
19574
19575                 return;
19576             }
19577
19578             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19579                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19580                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19581                 return;
19582             }
19583
19584             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19585             this.addItem(r.data);
19586             this.tickItems.push(r.data);
19587         }
19588     },
19589     
19590     getAutoCreateNativeIOS : function()
19591     {
19592         var cfg = {
19593             cls: 'form-group' //input-group,
19594         };
19595         
19596         var combobox =  {
19597             tag: 'select',
19598             cls : 'roo-ios-select'
19599         };
19600         
19601         if (this.name) {
19602             combobox.name = this.name;
19603         }
19604         
19605         if (this.disabled) {
19606             combobox.disabled = true;
19607         }
19608         
19609         var settings = this;
19610         
19611         ['xs','sm','md','lg'].map(function(size){
19612             if (settings[size]) {
19613                 cfg.cls += ' col-' + size + '-' + settings[size];
19614             }
19615         });
19616         
19617         cfg.cn = combobox;
19618         
19619         return cfg;
19620         
19621     },
19622     
19623     initIOSView : function()
19624     {
19625         this.store.on('load', this.onIOSViewLoad, this);
19626         
19627         return;
19628     },
19629     
19630     onIOSViewLoad : function()
19631     {
19632         if(this.store.getCount() < 1){
19633             return;
19634         }
19635         
19636         this.clearIOSView();
19637         
19638         if(this.allowBlank) {
19639             
19640             var default_text = '-- SELECT --';
19641             
19642             if(this.placeholder.length){
19643                 default_text = this.placeholder;
19644             }
19645             
19646             if(this.emptyTitle.length){
19647                 default_text += ' - ' + this.emptyTitle + ' -';
19648             }
19649             
19650             var opt = this.inputEl().createChild({
19651                 tag: 'option',
19652                 value : 0,
19653                 html : default_text
19654             });
19655             
19656             var o = {};
19657             o[this.valueField] = 0;
19658             o[this.displayField] = default_text;
19659             
19660             this.ios_options.push({
19661                 data : o,
19662                 el : opt
19663             });
19664             
19665         }
19666         
19667         this.store.data.each(function(d, rowIndex){
19668             
19669             var html = '';
19670             
19671             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19672                 html = d.data[this.displayField];
19673             }
19674             
19675             var value = '';
19676             
19677             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19678                 value = d.data[this.valueField];
19679             }
19680             
19681             var option = {
19682                 tag: 'option',
19683                 value : value,
19684                 html : html
19685             };
19686             
19687             if(this.value == d.data[this.valueField]){
19688                 option['selected'] = true;
19689             }
19690             
19691             var opt = this.inputEl().createChild(option);
19692             
19693             this.ios_options.push({
19694                 data : d.data,
19695                 el : opt
19696             });
19697             
19698         }, this);
19699         
19700         this.inputEl().on('change', function(){
19701            this.fireEvent('select', this);
19702         }, this);
19703         
19704     },
19705     
19706     clearIOSView: function()
19707     {
19708         this.inputEl().dom.innerHTML = '';
19709         
19710         this.ios_options = [];
19711     },
19712     
19713     setIOSValue: function(v)
19714     {
19715         this.value = v;
19716         
19717         if(!this.ios_options){
19718             return;
19719         }
19720         
19721         Roo.each(this.ios_options, function(opts){
19722            
19723            opts.el.dom.removeAttribute('selected');
19724            
19725            if(opts.data[this.valueField] != v){
19726                return;
19727            }
19728            
19729            opts.el.dom.setAttribute('selected', true);
19730            
19731         }, this);
19732     }
19733
19734     /** 
19735     * @cfg {Boolean} grow 
19736     * @hide 
19737     */
19738     /** 
19739     * @cfg {Number} growMin 
19740     * @hide 
19741     */
19742     /** 
19743     * @cfg {Number} growMax 
19744     * @hide 
19745     */
19746     /**
19747      * @hide
19748      * @method autoSize
19749      */
19750 });
19751
19752 Roo.apply(Roo.bootstrap.ComboBox,  {
19753     
19754     header : {
19755         tag: 'div',
19756         cls: 'modal-header',
19757         cn: [
19758             {
19759                 tag: 'h4',
19760                 cls: 'modal-title'
19761             }
19762         ]
19763     },
19764     
19765     body : {
19766         tag: 'div',
19767         cls: 'modal-body',
19768         cn: [
19769             {
19770                 tag: 'ul',
19771                 cls: 'list-group'
19772             }
19773         ]
19774     },
19775     
19776     listItemRadio : {
19777         tag: 'li',
19778         cls: 'list-group-item',
19779         cn: [
19780             {
19781                 tag: 'span',
19782                 cls: 'roo-combobox-list-group-item-value'
19783             },
19784             {
19785                 tag: 'div',
19786                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19787                 cn: [
19788                     {
19789                         tag: 'input',
19790                         type: 'radio'
19791                     },
19792                     {
19793                         tag: 'label'
19794                     }
19795                 ]
19796             }
19797         ]
19798     },
19799     
19800     listItemCheckbox : {
19801         tag: 'li',
19802         cls: 'list-group-item',
19803         cn: [
19804             {
19805                 tag: 'span',
19806                 cls: 'roo-combobox-list-group-item-value'
19807             },
19808             {
19809                 tag: 'div',
19810                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19811                 cn: [
19812                     {
19813                         tag: 'input',
19814                         type: 'checkbox'
19815                     },
19816                     {
19817                         tag: 'label'
19818                     }
19819                 ]
19820             }
19821         ]
19822     },
19823     
19824     emptyResult : {
19825         tag: 'div',
19826         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19827     },
19828     
19829     footer : {
19830         tag: 'div',
19831         cls: 'modal-footer',
19832         cn: [
19833             {
19834                 tag: 'div',
19835                 cls: 'row',
19836                 cn: [
19837                     {
19838                         tag: 'div',
19839                         cls: 'col-xs-6 text-left',
19840                         cn: {
19841                             tag: 'button',
19842                             cls: 'btn btn-danger roo-touch-view-cancel',
19843                             html: 'Cancel'
19844                         }
19845                     },
19846                     {
19847                         tag: 'div',
19848                         cls: 'col-xs-6 text-right',
19849                         cn: {
19850                             tag: 'button',
19851                             cls: 'btn btn-success roo-touch-view-ok',
19852                             html: 'OK'
19853                         }
19854                     }
19855                 ]
19856             }
19857         ]
19858         
19859     }
19860 });
19861
19862 Roo.apply(Roo.bootstrap.ComboBox,  {
19863     
19864     touchViewTemplate : {
19865         tag: 'div',
19866         cls: 'modal fade roo-combobox-touch-view',
19867         cn: [
19868             {
19869                 tag: 'div',
19870                 cls: 'modal-dialog',
19871                 style : 'position:fixed', // we have to fix position....
19872                 cn: [
19873                     {
19874                         tag: 'div',
19875                         cls: 'modal-content',
19876                         cn: [
19877                             Roo.bootstrap.ComboBox.header,
19878                             Roo.bootstrap.ComboBox.body,
19879                             Roo.bootstrap.ComboBox.footer
19880                         ]
19881                     }
19882                 ]
19883             }
19884         ]
19885     }
19886 });/*
19887  * Based on:
19888  * Ext JS Library 1.1.1
19889  * Copyright(c) 2006-2007, Ext JS, LLC.
19890  *
19891  * Originally Released Under LGPL - original licence link has changed is not relivant.
19892  *
19893  * Fork - LGPL
19894  * <script type="text/javascript">
19895  */
19896
19897 /**
19898  * @class Roo.View
19899  * @extends Roo.util.Observable
19900  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19901  * This class also supports single and multi selection modes. <br>
19902  * Create a data model bound view:
19903  <pre><code>
19904  var store = new Roo.data.Store(...);
19905
19906  var view = new Roo.View({
19907     el : "my-element",
19908     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19909  
19910     singleSelect: true,
19911     selectedClass: "ydataview-selected",
19912     store: store
19913  });
19914
19915  // listen for node click?
19916  view.on("click", function(vw, index, node, e){
19917  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19918  });
19919
19920  // load XML data
19921  dataModel.load("foobar.xml");
19922  </code></pre>
19923  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19924  * <br><br>
19925  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19926  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19927  * 
19928  * Note: old style constructor is still suported (container, template, config)
19929  * 
19930  * @constructor
19931  * Create a new View
19932  * @param {Object} config The config object
19933  * 
19934  */
19935 Roo.View = function(config, depreciated_tpl, depreciated_config){
19936     
19937     this.parent = false;
19938     
19939     if (typeof(depreciated_tpl) == 'undefined') {
19940         // new way.. - universal constructor.
19941         Roo.apply(this, config);
19942         this.el  = Roo.get(this.el);
19943     } else {
19944         // old format..
19945         this.el  = Roo.get(config);
19946         this.tpl = depreciated_tpl;
19947         Roo.apply(this, depreciated_config);
19948     }
19949     this.wrapEl  = this.el.wrap().wrap();
19950     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19951     
19952     
19953     if(typeof(this.tpl) == "string"){
19954         this.tpl = new Roo.Template(this.tpl);
19955     } else {
19956         // support xtype ctors..
19957         this.tpl = new Roo.factory(this.tpl, Roo);
19958     }
19959     
19960     
19961     this.tpl.compile();
19962     
19963     /** @private */
19964     this.addEvents({
19965         /**
19966          * @event beforeclick
19967          * Fires before a click is processed. Returns false to cancel the default action.
19968          * @param {Roo.View} this
19969          * @param {Number} index The index of the target node
19970          * @param {HTMLElement} node The target node
19971          * @param {Roo.EventObject} e The raw event object
19972          */
19973             "beforeclick" : true,
19974         /**
19975          * @event click
19976          * Fires when a template node is clicked.
19977          * @param {Roo.View} this
19978          * @param {Number} index The index of the target node
19979          * @param {HTMLElement} node The target node
19980          * @param {Roo.EventObject} e The raw event object
19981          */
19982             "click" : true,
19983         /**
19984          * @event dblclick
19985          * Fires when a template node is double clicked.
19986          * @param {Roo.View} this
19987          * @param {Number} index The index of the target node
19988          * @param {HTMLElement} node The target node
19989          * @param {Roo.EventObject} e The raw event object
19990          */
19991             "dblclick" : true,
19992         /**
19993          * @event contextmenu
19994          * Fires when a template node is right clicked.
19995          * @param {Roo.View} this
19996          * @param {Number} index The index of the target node
19997          * @param {HTMLElement} node The target node
19998          * @param {Roo.EventObject} e The raw event object
19999          */
20000             "contextmenu" : true,
20001         /**
20002          * @event selectionchange
20003          * Fires when the selected nodes change.
20004          * @param {Roo.View} this
20005          * @param {Array} selections Array of the selected nodes
20006          */
20007             "selectionchange" : true,
20008     
20009         /**
20010          * @event beforeselect
20011          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20012          * @param {Roo.View} this
20013          * @param {HTMLElement} node The node to be selected
20014          * @param {Array} selections Array of currently selected nodes
20015          */
20016             "beforeselect" : true,
20017         /**
20018          * @event preparedata
20019          * Fires on every row to render, to allow you to change the data.
20020          * @param {Roo.View} this
20021          * @param {Object} data to be rendered (change this)
20022          */
20023           "preparedata" : true
20024           
20025           
20026         });
20027
20028
20029
20030     this.el.on({
20031         "click": this.onClick,
20032         "dblclick": this.onDblClick,
20033         "contextmenu": this.onContextMenu,
20034         scope:this
20035     });
20036
20037     this.selections = [];
20038     this.nodes = [];
20039     this.cmp = new Roo.CompositeElementLite([]);
20040     if(this.store){
20041         this.store = Roo.factory(this.store, Roo.data);
20042         this.setStore(this.store, true);
20043     }
20044     
20045     if ( this.footer && this.footer.xtype) {
20046            
20047          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20048         
20049         this.footer.dataSource = this.store;
20050         this.footer.container = fctr;
20051         this.footer = Roo.factory(this.footer, Roo);
20052         fctr.insertFirst(this.el);
20053         
20054         // this is a bit insane - as the paging toolbar seems to detach the el..
20055 //        dom.parentNode.parentNode.parentNode
20056          // they get detached?
20057     }
20058     
20059     
20060     Roo.View.superclass.constructor.call(this);
20061     
20062     
20063 };
20064
20065 Roo.extend(Roo.View, Roo.util.Observable, {
20066     
20067      /**
20068      * @cfg {Roo.data.Store} store Data store to load data from.
20069      */
20070     store : false,
20071     
20072     /**
20073      * @cfg {String|Roo.Element} el The container element.
20074      */
20075     el : '',
20076     
20077     /**
20078      * @cfg {String|Roo.Template} tpl The template used by this View 
20079      */
20080     tpl : false,
20081     /**
20082      * @cfg {String} dataName the named area of the template to use as the data area
20083      *                          Works with domtemplates roo-name="name"
20084      */
20085     dataName: false,
20086     /**
20087      * @cfg {String} selectedClass The css class to add to selected nodes
20088      */
20089     selectedClass : "x-view-selected",
20090      /**
20091      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20092      */
20093     emptyText : "",
20094     
20095     /**
20096      * @cfg {String} text to display on mask (default Loading)
20097      */
20098     mask : false,
20099     /**
20100      * @cfg {Boolean} multiSelect Allow multiple selection
20101      */
20102     multiSelect : false,
20103     /**
20104      * @cfg {Boolean} singleSelect Allow single selection
20105      */
20106     singleSelect:  false,
20107     
20108     /**
20109      * @cfg {Boolean} toggleSelect - selecting 
20110      */
20111     toggleSelect : false,
20112     
20113     /**
20114      * @cfg {Boolean} tickable - selecting 
20115      */
20116     tickable : false,
20117     
20118     /**
20119      * Returns the element this view is bound to.
20120      * @return {Roo.Element}
20121      */
20122     getEl : function(){
20123         return this.wrapEl;
20124     },
20125     
20126     
20127
20128     /**
20129      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20130      */
20131     refresh : function(){
20132         //Roo.log('refresh');
20133         var t = this.tpl;
20134         
20135         // if we are using something like 'domtemplate', then
20136         // the what gets used is:
20137         // t.applySubtemplate(NAME, data, wrapping data..)
20138         // the outer template then get' applied with
20139         //     the store 'extra data'
20140         // and the body get's added to the
20141         //      roo-name="data" node?
20142         //      <span class='roo-tpl-{name}'></span> ?????
20143         
20144         
20145         
20146         this.clearSelections();
20147         this.el.update("");
20148         var html = [];
20149         var records = this.store.getRange();
20150         if(records.length < 1) {
20151             
20152             // is this valid??  = should it render a template??
20153             
20154             this.el.update(this.emptyText);
20155             return;
20156         }
20157         var el = this.el;
20158         if (this.dataName) {
20159             this.el.update(t.apply(this.store.meta)); //????
20160             el = this.el.child('.roo-tpl-' + this.dataName);
20161         }
20162         
20163         for(var i = 0, len = records.length; i < len; i++){
20164             var data = this.prepareData(records[i].data, i, records[i]);
20165             this.fireEvent("preparedata", this, data, i, records[i]);
20166             
20167             var d = Roo.apply({}, data);
20168             
20169             if(this.tickable){
20170                 Roo.apply(d, {'roo-id' : Roo.id()});
20171                 
20172                 var _this = this;
20173             
20174                 Roo.each(this.parent.item, function(item){
20175                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20176                         return;
20177                     }
20178                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20179                 });
20180             }
20181             
20182             html[html.length] = Roo.util.Format.trim(
20183                 this.dataName ?
20184                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20185                     t.apply(d)
20186             );
20187         }
20188         
20189         
20190         
20191         el.update(html.join(""));
20192         this.nodes = el.dom.childNodes;
20193         this.updateIndexes(0);
20194     },
20195     
20196
20197     /**
20198      * Function to override to reformat the data that is sent to
20199      * the template for each node.
20200      * DEPRICATED - use the preparedata event handler.
20201      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20202      * a JSON object for an UpdateManager bound view).
20203      */
20204     prepareData : function(data, index, record)
20205     {
20206         this.fireEvent("preparedata", this, data, index, record);
20207         return data;
20208     },
20209
20210     onUpdate : function(ds, record){
20211         // Roo.log('on update');   
20212         this.clearSelections();
20213         var index = this.store.indexOf(record);
20214         var n = this.nodes[index];
20215         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20216         n.parentNode.removeChild(n);
20217         this.updateIndexes(index, index);
20218     },
20219
20220     
20221     
20222 // --------- FIXME     
20223     onAdd : function(ds, records, index)
20224     {
20225         //Roo.log(['on Add', ds, records, index] );        
20226         this.clearSelections();
20227         if(this.nodes.length == 0){
20228             this.refresh();
20229             return;
20230         }
20231         var n = this.nodes[index];
20232         for(var i = 0, len = records.length; i < len; i++){
20233             var d = this.prepareData(records[i].data, i, records[i]);
20234             if(n){
20235                 this.tpl.insertBefore(n, d);
20236             }else{
20237                 
20238                 this.tpl.append(this.el, d);
20239             }
20240         }
20241         this.updateIndexes(index);
20242     },
20243
20244     onRemove : function(ds, record, index){
20245        // Roo.log('onRemove');
20246         this.clearSelections();
20247         var el = this.dataName  ?
20248             this.el.child('.roo-tpl-' + this.dataName) :
20249             this.el; 
20250         
20251         el.dom.removeChild(this.nodes[index]);
20252         this.updateIndexes(index);
20253     },
20254
20255     /**
20256      * Refresh an individual node.
20257      * @param {Number} index
20258      */
20259     refreshNode : function(index){
20260         this.onUpdate(this.store, this.store.getAt(index));
20261     },
20262
20263     updateIndexes : function(startIndex, endIndex){
20264         var ns = this.nodes;
20265         startIndex = startIndex || 0;
20266         endIndex = endIndex || ns.length - 1;
20267         for(var i = startIndex; i <= endIndex; i++){
20268             ns[i].nodeIndex = i;
20269         }
20270     },
20271
20272     /**
20273      * Changes the data store this view uses and refresh the view.
20274      * @param {Store} store
20275      */
20276     setStore : function(store, initial){
20277         if(!initial && this.store){
20278             this.store.un("datachanged", this.refresh);
20279             this.store.un("add", this.onAdd);
20280             this.store.un("remove", this.onRemove);
20281             this.store.un("update", this.onUpdate);
20282             this.store.un("clear", this.refresh);
20283             this.store.un("beforeload", this.onBeforeLoad);
20284             this.store.un("load", this.onLoad);
20285             this.store.un("loadexception", this.onLoad);
20286         }
20287         if(store){
20288           
20289             store.on("datachanged", this.refresh, this);
20290             store.on("add", this.onAdd, this);
20291             store.on("remove", this.onRemove, this);
20292             store.on("update", this.onUpdate, this);
20293             store.on("clear", this.refresh, this);
20294             store.on("beforeload", this.onBeforeLoad, this);
20295             store.on("load", this.onLoad, this);
20296             store.on("loadexception", this.onLoad, this);
20297         }
20298         
20299         if(store){
20300             this.refresh();
20301         }
20302     },
20303     /**
20304      * onbeforeLoad - masks the loading area.
20305      *
20306      */
20307     onBeforeLoad : function(store,opts)
20308     {
20309          //Roo.log('onBeforeLoad');   
20310         if (!opts.add) {
20311             this.el.update("");
20312         }
20313         this.el.mask(this.mask ? this.mask : "Loading" ); 
20314     },
20315     onLoad : function ()
20316     {
20317         this.el.unmask();
20318     },
20319     
20320
20321     /**
20322      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20323      * @param {HTMLElement} node
20324      * @return {HTMLElement} The template node
20325      */
20326     findItemFromChild : function(node){
20327         var el = this.dataName  ?
20328             this.el.child('.roo-tpl-' + this.dataName,true) :
20329             this.el.dom; 
20330         
20331         if(!node || node.parentNode == el){
20332                     return node;
20333             }
20334             var p = node.parentNode;
20335             while(p && p != el){
20336             if(p.parentNode == el){
20337                 return p;
20338             }
20339             p = p.parentNode;
20340         }
20341             return null;
20342     },
20343
20344     /** @ignore */
20345     onClick : function(e){
20346         var item = this.findItemFromChild(e.getTarget());
20347         if(item){
20348             var index = this.indexOf(item);
20349             if(this.onItemClick(item, index, e) !== false){
20350                 this.fireEvent("click", this, index, item, e);
20351             }
20352         }else{
20353             this.clearSelections();
20354         }
20355     },
20356
20357     /** @ignore */
20358     onContextMenu : function(e){
20359         var item = this.findItemFromChild(e.getTarget());
20360         if(item){
20361             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20362         }
20363     },
20364
20365     /** @ignore */
20366     onDblClick : function(e){
20367         var item = this.findItemFromChild(e.getTarget());
20368         if(item){
20369             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20370         }
20371     },
20372
20373     onItemClick : function(item, index, e)
20374     {
20375         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20376             return false;
20377         }
20378         if (this.toggleSelect) {
20379             var m = this.isSelected(item) ? 'unselect' : 'select';
20380             //Roo.log(m);
20381             var _t = this;
20382             _t[m](item, true, false);
20383             return true;
20384         }
20385         if(this.multiSelect || this.singleSelect){
20386             if(this.multiSelect && e.shiftKey && this.lastSelection){
20387                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20388             }else{
20389                 this.select(item, this.multiSelect && e.ctrlKey);
20390                 this.lastSelection = item;
20391             }
20392             
20393             if(!this.tickable){
20394                 e.preventDefault();
20395             }
20396             
20397         }
20398         return true;
20399     },
20400
20401     /**
20402      * Get the number of selected nodes.
20403      * @return {Number}
20404      */
20405     getSelectionCount : function(){
20406         return this.selections.length;
20407     },
20408
20409     /**
20410      * Get the currently selected nodes.
20411      * @return {Array} An array of HTMLElements
20412      */
20413     getSelectedNodes : function(){
20414         return this.selections;
20415     },
20416
20417     /**
20418      * Get the indexes of the selected nodes.
20419      * @return {Array}
20420      */
20421     getSelectedIndexes : function(){
20422         var indexes = [], s = this.selections;
20423         for(var i = 0, len = s.length; i < len; i++){
20424             indexes.push(s[i].nodeIndex);
20425         }
20426         return indexes;
20427     },
20428
20429     /**
20430      * Clear all selections
20431      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20432      */
20433     clearSelections : function(suppressEvent){
20434         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20435             this.cmp.elements = this.selections;
20436             this.cmp.removeClass(this.selectedClass);
20437             this.selections = [];
20438             if(!suppressEvent){
20439                 this.fireEvent("selectionchange", this, this.selections);
20440             }
20441         }
20442     },
20443
20444     /**
20445      * Returns true if the passed node is selected
20446      * @param {HTMLElement/Number} node The node or node index
20447      * @return {Boolean}
20448      */
20449     isSelected : function(node){
20450         var s = this.selections;
20451         if(s.length < 1){
20452             return false;
20453         }
20454         node = this.getNode(node);
20455         return s.indexOf(node) !== -1;
20456     },
20457
20458     /**
20459      * Selects nodes.
20460      * @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
20461      * @param {Boolean} keepExisting (optional) true to keep existing selections
20462      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20463      */
20464     select : function(nodeInfo, keepExisting, suppressEvent){
20465         if(nodeInfo instanceof Array){
20466             if(!keepExisting){
20467                 this.clearSelections(true);
20468             }
20469             for(var i = 0, len = nodeInfo.length; i < len; i++){
20470                 this.select(nodeInfo[i], true, true);
20471             }
20472             return;
20473         } 
20474         var node = this.getNode(nodeInfo);
20475         if(!node || this.isSelected(node)){
20476             return; // already selected.
20477         }
20478         if(!keepExisting){
20479             this.clearSelections(true);
20480         }
20481         
20482         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20483             Roo.fly(node).addClass(this.selectedClass);
20484             this.selections.push(node);
20485             if(!suppressEvent){
20486                 this.fireEvent("selectionchange", this, this.selections);
20487             }
20488         }
20489         
20490         
20491     },
20492       /**
20493      * Unselects nodes.
20494      * @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
20495      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20496      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20497      */
20498     unselect : function(nodeInfo, keepExisting, suppressEvent)
20499     {
20500         if(nodeInfo instanceof Array){
20501             Roo.each(this.selections, function(s) {
20502                 this.unselect(s, nodeInfo);
20503             }, this);
20504             return;
20505         }
20506         var node = this.getNode(nodeInfo);
20507         if(!node || !this.isSelected(node)){
20508             //Roo.log("not selected");
20509             return; // not selected.
20510         }
20511         // fireevent???
20512         var ns = [];
20513         Roo.each(this.selections, function(s) {
20514             if (s == node ) {
20515                 Roo.fly(node).removeClass(this.selectedClass);
20516
20517                 return;
20518             }
20519             ns.push(s);
20520         },this);
20521         
20522         this.selections= ns;
20523         this.fireEvent("selectionchange", this, this.selections);
20524     },
20525
20526     /**
20527      * Gets a template node.
20528      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20529      * @return {HTMLElement} The node or null if it wasn't found
20530      */
20531     getNode : function(nodeInfo){
20532         if(typeof nodeInfo == "string"){
20533             return document.getElementById(nodeInfo);
20534         }else if(typeof nodeInfo == "number"){
20535             return this.nodes[nodeInfo];
20536         }
20537         return nodeInfo;
20538     },
20539
20540     /**
20541      * Gets a range template nodes.
20542      * @param {Number} startIndex
20543      * @param {Number} endIndex
20544      * @return {Array} An array of nodes
20545      */
20546     getNodes : function(start, end){
20547         var ns = this.nodes;
20548         start = start || 0;
20549         end = typeof end == "undefined" ? ns.length - 1 : end;
20550         var nodes = [];
20551         if(start <= end){
20552             for(var i = start; i <= end; i++){
20553                 nodes.push(ns[i]);
20554             }
20555         } else{
20556             for(var i = start; i >= end; i--){
20557                 nodes.push(ns[i]);
20558             }
20559         }
20560         return nodes;
20561     },
20562
20563     /**
20564      * Finds the index of the passed node
20565      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20566      * @return {Number} The index of the node or -1
20567      */
20568     indexOf : function(node){
20569         node = this.getNode(node);
20570         if(typeof node.nodeIndex == "number"){
20571             return node.nodeIndex;
20572         }
20573         var ns = this.nodes;
20574         for(var i = 0, len = ns.length; i < len; i++){
20575             if(ns[i] == node){
20576                 return i;
20577             }
20578         }
20579         return -1;
20580     }
20581 });
20582 /*
20583  * - LGPL
20584  *
20585  * based on jquery fullcalendar
20586  * 
20587  */
20588
20589 Roo.bootstrap = Roo.bootstrap || {};
20590 /**
20591  * @class Roo.bootstrap.Calendar
20592  * @extends Roo.bootstrap.Component
20593  * Bootstrap Calendar class
20594  * @cfg {Boolean} loadMask (true|false) default false
20595  * @cfg {Object} header generate the user specific header of the calendar, default false
20596
20597  * @constructor
20598  * Create a new Container
20599  * @param {Object} config The config object
20600  */
20601
20602
20603
20604 Roo.bootstrap.Calendar = function(config){
20605     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20606      this.addEvents({
20607         /**
20608              * @event select
20609              * Fires when a date is selected
20610              * @param {DatePicker} this
20611              * @param {Date} date The selected date
20612              */
20613         'select': true,
20614         /**
20615              * @event monthchange
20616              * Fires when the displayed month changes 
20617              * @param {DatePicker} this
20618              * @param {Date} date The selected month
20619              */
20620         'monthchange': true,
20621         /**
20622              * @event evententer
20623              * Fires when mouse over an event
20624              * @param {Calendar} this
20625              * @param {event} Event
20626              */
20627         'evententer': true,
20628         /**
20629              * @event eventleave
20630              * Fires when the mouse leaves an
20631              * @param {Calendar} this
20632              * @param {event}
20633              */
20634         'eventleave': true,
20635         /**
20636              * @event eventclick
20637              * Fires when the mouse click an
20638              * @param {Calendar} this
20639              * @param {event}
20640              */
20641         'eventclick': true
20642         
20643     });
20644
20645 };
20646
20647 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20648     
20649           /**
20650      * @cfg {Roo.data.Store} store
20651      * The data source for the calendar
20652      */
20653         store : false,
20654      /**
20655      * @cfg {Number} startDay
20656      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20657      */
20658     startDay : 0,
20659     
20660     loadMask : false,
20661     
20662     header : false,
20663       
20664     getAutoCreate : function(){
20665         
20666         
20667         var fc_button = function(name, corner, style, content ) {
20668             return Roo.apply({},{
20669                 tag : 'span',
20670                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20671                          (corner.length ?
20672                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20673                             ''
20674                         ),
20675                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20676                 unselectable: 'on'
20677             });
20678         };
20679         
20680         var header = {};
20681         
20682         if(!this.header){
20683             header = {
20684                 tag : 'table',
20685                 cls : 'fc-header',
20686                 style : 'width:100%',
20687                 cn : [
20688                     {
20689                         tag: 'tr',
20690                         cn : [
20691                             {
20692                                 tag : 'td',
20693                                 cls : 'fc-header-left',
20694                                 cn : [
20695                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20696                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20697                                     { tag: 'span', cls: 'fc-header-space' },
20698                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20699
20700
20701                                 ]
20702                             },
20703
20704                             {
20705                                 tag : 'td',
20706                                 cls : 'fc-header-center',
20707                                 cn : [
20708                                     {
20709                                         tag: 'span',
20710                                         cls: 'fc-header-title',
20711                                         cn : {
20712                                             tag: 'H2',
20713                                             html : 'month / year'
20714                                         }
20715                                     }
20716
20717                                 ]
20718                             },
20719                             {
20720                                 tag : 'td',
20721                                 cls : 'fc-header-right',
20722                                 cn : [
20723                               /*      fc_button('month', 'left', '', 'month' ),
20724                                     fc_button('week', '', '', 'week' ),
20725                                     fc_button('day', 'right', '', 'day' )
20726                                 */    
20727
20728                                 ]
20729                             }
20730
20731                         ]
20732                     }
20733                 ]
20734             };
20735         }
20736         
20737         header = this.header;
20738         
20739        
20740         var cal_heads = function() {
20741             var ret = [];
20742             // fixme - handle this.
20743             
20744             for (var i =0; i < Date.dayNames.length; i++) {
20745                 var d = Date.dayNames[i];
20746                 ret.push({
20747                     tag: 'th',
20748                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20749                     html : d.substring(0,3)
20750                 });
20751                 
20752             }
20753             ret[0].cls += ' fc-first';
20754             ret[6].cls += ' fc-last';
20755             return ret;
20756         };
20757         var cal_cell = function(n) {
20758             return  {
20759                 tag: 'td',
20760                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20761                 cn : [
20762                     {
20763                         cn : [
20764                             {
20765                                 cls: 'fc-day-number',
20766                                 html: 'D'
20767                             },
20768                             {
20769                                 cls: 'fc-day-content',
20770                              
20771                                 cn : [
20772                                      {
20773                                         style: 'position: relative;' // height: 17px;
20774                                     }
20775                                 ]
20776                             }
20777                             
20778                             
20779                         ]
20780                     }
20781                 ]
20782                 
20783             }
20784         };
20785         var cal_rows = function() {
20786             
20787             var ret = [];
20788             for (var r = 0; r < 6; r++) {
20789                 var row= {
20790                     tag : 'tr',
20791                     cls : 'fc-week',
20792                     cn : []
20793                 };
20794                 
20795                 for (var i =0; i < Date.dayNames.length; i++) {
20796                     var d = Date.dayNames[i];
20797                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20798
20799                 }
20800                 row.cn[0].cls+=' fc-first';
20801                 row.cn[0].cn[0].style = 'min-height:90px';
20802                 row.cn[6].cls+=' fc-last';
20803                 ret.push(row);
20804                 
20805             }
20806             ret[0].cls += ' fc-first';
20807             ret[4].cls += ' fc-prev-last';
20808             ret[5].cls += ' fc-last';
20809             return ret;
20810             
20811         };
20812         
20813         var cal_table = {
20814             tag: 'table',
20815             cls: 'fc-border-separate',
20816             style : 'width:100%',
20817             cellspacing  : 0,
20818             cn : [
20819                 { 
20820                     tag: 'thead',
20821                     cn : [
20822                         { 
20823                             tag: 'tr',
20824                             cls : 'fc-first fc-last',
20825                             cn : cal_heads()
20826                         }
20827                     ]
20828                 },
20829                 { 
20830                     tag: 'tbody',
20831                     cn : cal_rows()
20832                 }
20833                   
20834             ]
20835         };
20836          
20837          var cfg = {
20838             cls : 'fc fc-ltr',
20839             cn : [
20840                 header,
20841                 {
20842                     cls : 'fc-content',
20843                     style : "position: relative;",
20844                     cn : [
20845                         {
20846                             cls : 'fc-view fc-view-month fc-grid',
20847                             style : 'position: relative',
20848                             unselectable : 'on',
20849                             cn : [
20850                                 {
20851                                     cls : 'fc-event-container',
20852                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20853                                 },
20854                                 cal_table
20855                             ]
20856                         }
20857                     ]
20858     
20859                 }
20860            ] 
20861             
20862         };
20863         
20864          
20865         
20866         return cfg;
20867     },
20868     
20869     
20870     initEvents : function()
20871     {
20872         if(!this.store){
20873             throw "can not find store for calendar";
20874         }
20875         
20876         var mark = {
20877             tag: "div",
20878             cls:"x-dlg-mask",
20879             style: "text-align:center",
20880             cn: [
20881                 {
20882                     tag: "div",
20883                     style: "background-color:white;width:50%;margin:250 auto",
20884                     cn: [
20885                         {
20886                             tag: "img",
20887                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20888                         },
20889                         {
20890                             tag: "span",
20891                             html: "Loading"
20892                         }
20893                         
20894                     ]
20895                 }
20896             ]
20897         };
20898         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20899         
20900         var size = this.el.select('.fc-content', true).first().getSize();
20901         this.maskEl.setSize(size.width, size.height);
20902         this.maskEl.enableDisplayMode("block");
20903         if(!this.loadMask){
20904             this.maskEl.hide();
20905         }
20906         
20907         this.store = Roo.factory(this.store, Roo.data);
20908         this.store.on('load', this.onLoad, this);
20909         this.store.on('beforeload', this.onBeforeLoad, this);
20910         
20911         this.resize();
20912         
20913         this.cells = this.el.select('.fc-day',true);
20914         //Roo.log(this.cells);
20915         this.textNodes = this.el.query('.fc-day-number');
20916         this.cells.addClassOnOver('fc-state-hover');
20917         
20918         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20919         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20920         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20921         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20922         
20923         this.on('monthchange', this.onMonthChange, this);
20924         
20925         this.update(new Date().clearTime());
20926     },
20927     
20928     resize : function() {
20929         var sz  = this.el.getSize();
20930         
20931         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20932         this.el.select('.fc-day-content div',true).setHeight(34);
20933     },
20934     
20935     
20936     // private
20937     showPrevMonth : function(e){
20938         this.update(this.activeDate.add("mo", -1));
20939     },
20940     showToday : function(e){
20941         this.update(new Date().clearTime());
20942     },
20943     // private
20944     showNextMonth : function(e){
20945         this.update(this.activeDate.add("mo", 1));
20946     },
20947
20948     // private
20949     showPrevYear : function(){
20950         this.update(this.activeDate.add("y", -1));
20951     },
20952
20953     // private
20954     showNextYear : function(){
20955         this.update(this.activeDate.add("y", 1));
20956     },
20957
20958     
20959    // private
20960     update : function(date)
20961     {
20962         var vd = this.activeDate;
20963         this.activeDate = date;
20964 //        if(vd && this.el){
20965 //            var t = date.getTime();
20966 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20967 //                Roo.log('using add remove');
20968 //                
20969 //                this.fireEvent('monthchange', this, date);
20970 //                
20971 //                this.cells.removeClass("fc-state-highlight");
20972 //                this.cells.each(function(c){
20973 //                   if(c.dateValue == t){
20974 //                       c.addClass("fc-state-highlight");
20975 //                       setTimeout(function(){
20976 //                            try{c.dom.firstChild.focus();}catch(e){}
20977 //                       }, 50);
20978 //                       return false;
20979 //                   }
20980 //                   return true;
20981 //                });
20982 //                return;
20983 //            }
20984 //        }
20985         
20986         var days = date.getDaysInMonth();
20987         
20988         var firstOfMonth = date.getFirstDateOfMonth();
20989         var startingPos = firstOfMonth.getDay()-this.startDay;
20990         
20991         if(startingPos < this.startDay){
20992             startingPos += 7;
20993         }
20994         
20995         var pm = date.add(Date.MONTH, -1);
20996         var prevStart = pm.getDaysInMonth()-startingPos;
20997 //        
20998         this.cells = this.el.select('.fc-day',true);
20999         this.textNodes = this.el.query('.fc-day-number');
21000         this.cells.addClassOnOver('fc-state-hover');
21001         
21002         var cells = this.cells.elements;
21003         var textEls = this.textNodes;
21004         
21005         Roo.each(cells, function(cell){
21006             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21007         });
21008         
21009         days += startingPos;
21010
21011         // convert everything to numbers so it's fast
21012         var day = 86400000;
21013         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21014         //Roo.log(d);
21015         //Roo.log(pm);
21016         //Roo.log(prevStart);
21017         
21018         var today = new Date().clearTime().getTime();
21019         var sel = date.clearTime().getTime();
21020         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21021         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21022         var ddMatch = this.disabledDatesRE;
21023         var ddText = this.disabledDatesText;
21024         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21025         var ddaysText = this.disabledDaysText;
21026         var format = this.format;
21027         
21028         var setCellClass = function(cal, cell){
21029             cell.row = 0;
21030             cell.events = [];
21031             cell.more = [];
21032             //Roo.log('set Cell Class');
21033             cell.title = "";
21034             var t = d.getTime();
21035             
21036             //Roo.log(d);
21037             
21038             cell.dateValue = t;
21039             if(t == today){
21040                 cell.className += " fc-today";
21041                 cell.className += " fc-state-highlight";
21042                 cell.title = cal.todayText;
21043             }
21044             if(t == sel){
21045                 // disable highlight in other month..
21046                 //cell.className += " fc-state-highlight";
21047                 
21048             }
21049             // disabling
21050             if(t < min) {
21051                 cell.className = " fc-state-disabled";
21052                 cell.title = cal.minText;
21053                 return;
21054             }
21055             if(t > max) {
21056                 cell.className = " fc-state-disabled";
21057                 cell.title = cal.maxText;
21058                 return;
21059             }
21060             if(ddays){
21061                 if(ddays.indexOf(d.getDay()) != -1){
21062                     cell.title = ddaysText;
21063                     cell.className = " fc-state-disabled";
21064                 }
21065             }
21066             if(ddMatch && format){
21067                 var fvalue = d.dateFormat(format);
21068                 if(ddMatch.test(fvalue)){
21069                     cell.title = ddText.replace("%0", fvalue);
21070                     cell.className = " fc-state-disabled";
21071                 }
21072             }
21073             
21074             if (!cell.initialClassName) {
21075                 cell.initialClassName = cell.dom.className;
21076             }
21077             
21078             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21079         };
21080
21081         var i = 0;
21082         
21083         for(; i < startingPos; i++) {
21084             textEls[i].innerHTML = (++prevStart);
21085             d.setDate(d.getDate()+1);
21086             
21087             cells[i].className = "fc-past fc-other-month";
21088             setCellClass(this, cells[i]);
21089         }
21090         
21091         var intDay = 0;
21092         
21093         for(; i < days; i++){
21094             intDay = i - startingPos + 1;
21095             textEls[i].innerHTML = (intDay);
21096             d.setDate(d.getDate()+1);
21097             
21098             cells[i].className = ''; // "x-date-active";
21099             setCellClass(this, cells[i]);
21100         }
21101         var extraDays = 0;
21102         
21103         for(; i < 42; i++) {
21104             textEls[i].innerHTML = (++extraDays);
21105             d.setDate(d.getDate()+1);
21106             
21107             cells[i].className = "fc-future fc-other-month";
21108             setCellClass(this, cells[i]);
21109         }
21110         
21111         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21112         
21113         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21114         
21115         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21116         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21117         
21118         if(totalRows != 6){
21119             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21120             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21121         }
21122         
21123         this.fireEvent('monthchange', this, date);
21124         
21125         
21126         /*
21127         if(!this.internalRender){
21128             var main = this.el.dom.firstChild;
21129             var w = main.offsetWidth;
21130             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21131             Roo.fly(main).setWidth(w);
21132             this.internalRender = true;
21133             // opera does not respect the auto grow header center column
21134             // then, after it gets a width opera refuses to recalculate
21135             // without a second pass
21136             if(Roo.isOpera && !this.secondPass){
21137                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21138                 this.secondPass = true;
21139                 this.update.defer(10, this, [date]);
21140             }
21141         }
21142         */
21143         
21144     },
21145     
21146     findCell : function(dt) {
21147         dt = dt.clearTime().getTime();
21148         var ret = false;
21149         this.cells.each(function(c){
21150             //Roo.log("check " +c.dateValue + '?=' + dt);
21151             if(c.dateValue == dt){
21152                 ret = c;
21153                 return false;
21154             }
21155             return true;
21156         });
21157         
21158         return ret;
21159     },
21160     
21161     findCells : function(ev) {
21162         var s = ev.start.clone().clearTime().getTime();
21163        // Roo.log(s);
21164         var e= ev.end.clone().clearTime().getTime();
21165        // Roo.log(e);
21166         var ret = [];
21167         this.cells.each(function(c){
21168              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21169             
21170             if(c.dateValue > e){
21171                 return ;
21172             }
21173             if(c.dateValue < s){
21174                 return ;
21175             }
21176             ret.push(c);
21177         });
21178         
21179         return ret;    
21180     },
21181     
21182 //    findBestRow: function(cells)
21183 //    {
21184 //        var ret = 0;
21185 //        
21186 //        for (var i =0 ; i < cells.length;i++) {
21187 //            ret  = Math.max(cells[i].rows || 0,ret);
21188 //        }
21189 //        return ret;
21190 //        
21191 //    },
21192     
21193     
21194     addItem : function(ev)
21195     {
21196         // look for vertical location slot in
21197         var cells = this.findCells(ev);
21198         
21199 //        ev.row = this.findBestRow(cells);
21200         
21201         // work out the location.
21202         
21203         var crow = false;
21204         var rows = [];
21205         for(var i =0; i < cells.length; i++) {
21206             
21207             cells[i].row = cells[0].row;
21208             
21209             if(i == 0){
21210                 cells[i].row = cells[i].row + 1;
21211             }
21212             
21213             if (!crow) {
21214                 crow = {
21215                     start : cells[i],
21216                     end :  cells[i]
21217                 };
21218                 continue;
21219             }
21220             if (crow.start.getY() == cells[i].getY()) {
21221                 // on same row.
21222                 crow.end = cells[i];
21223                 continue;
21224             }
21225             // different row.
21226             rows.push(crow);
21227             crow = {
21228                 start: cells[i],
21229                 end : cells[i]
21230             };
21231             
21232         }
21233         
21234         rows.push(crow);
21235         ev.els = [];
21236         ev.rows = rows;
21237         ev.cells = cells;
21238         
21239         cells[0].events.push(ev);
21240         
21241         this.calevents.push(ev);
21242     },
21243     
21244     clearEvents: function() {
21245         
21246         if(!this.calevents){
21247             return;
21248         }
21249         
21250         Roo.each(this.cells.elements, function(c){
21251             c.row = 0;
21252             c.events = [];
21253             c.more = [];
21254         });
21255         
21256         Roo.each(this.calevents, function(e) {
21257             Roo.each(e.els, function(el) {
21258                 el.un('mouseenter' ,this.onEventEnter, this);
21259                 el.un('mouseleave' ,this.onEventLeave, this);
21260                 el.remove();
21261             },this);
21262         },this);
21263         
21264         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21265             e.remove();
21266         });
21267         
21268     },
21269     
21270     renderEvents: function()
21271     {   
21272         var _this = this;
21273         
21274         this.cells.each(function(c) {
21275             
21276             if(c.row < 5){
21277                 return;
21278             }
21279             
21280             var ev = c.events;
21281             
21282             var r = 4;
21283             if(c.row != c.events.length){
21284                 r = 4 - (4 - (c.row - c.events.length));
21285             }
21286             
21287             c.events = ev.slice(0, r);
21288             c.more = ev.slice(r);
21289             
21290             if(c.more.length && c.more.length == 1){
21291                 c.events.push(c.more.pop());
21292             }
21293             
21294             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21295             
21296         });
21297             
21298         this.cells.each(function(c) {
21299             
21300             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21301             
21302             
21303             for (var e = 0; e < c.events.length; e++){
21304                 var ev = c.events[e];
21305                 var rows = ev.rows;
21306                 
21307                 for(var i = 0; i < rows.length; i++) {
21308                 
21309                     // how many rows should it span..
21310
21311                     var  cfg = {
21312                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21313                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21314
21315                         unselectable : "on",
21316                         cn : [
21317                             {
21318                                 cls: 'fc-event-inner',
21319                                 cn : [
21320     //                                {
21321     //                                  tag:'span',
21322     //                                  cls: 'fc-event-time',
21323     //                                  html : cells.length > 1 ? '' : ev.time
21324     //                                },
21325                                     {
21326                                       tag:'span',
21327                                       cls: 'fc-event-title',
21328                                       html : String.format('{0}', ev.title)
21329                                     }
21330
21331
21332                                 ]
21333                             },
21334                             {
21335                                 cls: 'ui-resizable-handle ui-resizable-e',
21336                                 html : '&nbsp;&nbsp;&nbsp'
21337                             }
21338
21339                         ]
21340                     };
21341
21342                     if (i == 0) {
21343                         cfg.cls += ' fc-event-start';
21344                     }
21345                     if ((i+1) == rows.length) {
21346                         cfg.cls += ' fc-event-end';
21347                     }
21348
21349                     var ctr = _this.el.select('.fc-event-container',true).first();
21350                     var cg = ctr.createChild(cfg);
21351
21352                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21353                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21354
21355                     var r = (c.more.length) ? 1 : 0;
21356                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21357                     cg.setWidth(ebox.right - sbox.x -2);
21358
21359                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21360                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21361                     cg.on('click', _this.onEventClick, _this, ev);
21362
21363                     ev.els.push(cg);
21364                     
21365                 }
21366                 
21367             }
21368             
21369             
21370             if(c.more.length){
21371                 var  cfg = {
21372                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21373                     style : 'position: absolute',
21374                     unselectable : "on",
21375                     cn : [
21376                         {
21377                             cls: 'fc-event-inner',
21378                             cn : [
21379                                 {
21380                                   tag:'span',
21381                                   cls: 'fc-event-title',
21382                                   html : 'More'
21383                                 }
21384
21385
21386                             ]
21387                         },
21388                         {
21389                             cls: 'ui-resizable-handle ui-resizable-e',
21390                             html : '&nbsp;&nbsp;&nbsp'
21391                         }
21392
21393                     ]
21394                 };
21395
21396                 var ctr = _this.el.select('.fc-event-container',true).first();
21397                 var cg = ctr.createChild(cfg);
21398
21399                 var sbox = c.select('.fc-day-content',true).first().getBox();
21400                 var ebox = c.select('.fc-day-content',true).first().getBox();
21401                 //Roo.log(cg);
21402                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21403                 cg.setWidth(ebox.right - sbox.x -2);
21404
21405                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21406                 
21407             }
21408             
21409         });
21410         
21411         
21412         
21413     },
21414     
21415     onEventEnter: function (e, el,event,d) {
21416         this.fireEvent('evententer', this, el, event);
21417     },
21418     
21419     onEventLeave: function (e, el,event,d) {
21420         this.fireEvent('eventleave', this, el, event);
21421     },
21422     
21423     onEventClick: function (e, el,event,d) {
21424         this.fireEvent('eventclick', this, el, event);
21425     },
21426     
21427     onMonthChange: function () {
21428         this.store.load();
21429     },
21430     
21431     onMoreEventClick: function(e, el, more)
21432     {
21433         var _this = this;
21434         
21435         this.calpopover.placement = 'right';
21436         this.calpopover.setTitle('More');
21437         
21438         this.calpopover.setContent('');
21439         
21440         var ctr = this.calpopover.el.select('.popover-content', true).first();
21441         
21442         Roo.each(more, function(m){
21443             var cfg = {
21444                 cls : 'fc-event-hori fc-event-draggable',
21445                 html : m.title
21446             };
21447             var cg = ctr.createChild(cfg);
21448             
21449             cg.on('click', _this.onEventClick, _this, m);
21450         });
21451         
21452         this.calpopover.show(el);
21453         
21454         
21455     },
21456     
21457     onLoad: function () 
21458     {   
21459         this.calevents = [];
21460         var cal = this;
21461         
21462         if(this.store.getCount() > 0){
21463             this.store.data.each(function(d){
21464                cal.addItem({
21465                     id : d.data.id,
21466                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21467                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21468                     time : d.data.start_time,
21469                     title : d.data.title,
21470                     description : d.data.description,
21471                     venue : d.data.venue
21472                 });
21473             });
21474         }
21475         
21476         this.renderEvents();
21477         
21478         if(this.calevents.length && this.loadMask){
21479             this.maskEl.hide();
21480         }
21481     },
21482     
21483     onBeforeLoad: function()
21484     {
21485         this.clearEvents();
21486         if(this.loadMask){
21487             this.maskEl.show();
21488         }
21489     }
21490 });
21491
21492  
21493  /*
21494  * - LGPL
21495  *
21496  * element
21497  * 
21498  */
21499
21500 /**
21501  * @class Roo.bootstrap.Popover
21502  * @extends Roo.bootstrap.Component
21503  * @builder-top
21504  * @children Roo.bootstrap.Component
21505  * Bootstrap Popover class
21506  * @cfg {String} html contents of the popover   (or false to use children..)
21507  * @cfg {String} title of popover (or false to hide)
21508  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21509  * @cfg {String} trigger click || hover (or false to trigger manually)
21510  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21511  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21512  *      - if false and it has a 'parent' then it will be automatically added to that element
21513  *      - if string - Roo.get  will be called 
21514  * @cfg {Number} delay - delay before showing
21515  
21516  * @constructor
21517  * Create a new Popover
21518  * @param {Object} config The config object
21519  */
21520
21521 Roo.bootstrap.Popover = function(config){
21522     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21523     
21524     this.addEvents({
21525         // raw events
21526          /**
21527          * @event show
21528          * After the popover show
21529          * 
21530          * @param {Roo.bootstrap.Popover} this
21531          */
21532         "show" : true,
21533         /**
21534          * @event hide
21535          * After the popover hide
21536          * 
21537          * @param {Roo.bootstrap.Popover} this
21538          */
21539         "hide" : true
21540     });
21541 };
21542
21543 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21544     
21545     title: false,
21546     html: false,
21547     
21548     placement : 'right',
21549     trigger : 'hover', // hover
21550     modal : false,
21551     delay : 0,
21552     
21553     over: false,
21554     
21555     can_build_overlaid : false,
21556     
21557     maskEl : false, // the mask element
21558     headerEl : false,
21559     contentEl : false,
21560     alignEl : false, // when show is called with an element - this get's stored.
21561     
21562     getChildContainer : function()
21563     {
21564         return this.contentEl;
21565         
21566     },
21567     getPopoverHeader : function()
21568     {
21569         this.title = true; // flag not to hide it..
21570         this.headerEl.addClass('p-0');
21571         return this.headerEl
21572     },
21573     
21574     
21575     getAutoCreate : function(){
21576          
21577         var cfg = {
21578            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21579            style: 'display:block',
21580            cn : [
21581                 {
21582                     cls : 'arrow'
21583                 },
21584                 {
21585                     cls : 'popover-inner ',
21586                     cn : [
21587                         {
21588                             tag: 'h3',
21589                             cls: 'popover-title popover-header',
21590                             html : this.title === false ? '' : this.title
21591                         },
21592                         {
21593                             cls : 'popover-content popover-body '  + (this.cls || ''),
21594                             html : this.html || ''
21595                         }
21596                     ]
21597                     
21598                 }
21599            ]
21600         };
21601         
21602         return cfg;
21603     },
21604     /**
21605      * @param {string} the title
21606      */
21607     setTitle: function(str)
21608     {
21609         this.title = str;
21610         if (this.el) {
21611             this.headerEl.dom.innerHTML = str;
21612         }
21613         
21614     },
21615     /**
21616      * @param {string} the body content
21617      */
21618     setContent: function(str)
21619     {
21620         this.html = str;
21621         if (this.contentEl) {
21622             this.contentEl.dom.innerHTML = str;
21623         }
21624         
21625     },
21626     // as it get's added to the bottom of the page.
21627     onRender : function(ct, position)
21628     {
21629         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21630         
21631         
21632         
21633         if(!this.el){
21634             var cfg = Roo.apply({},  this.getAutoCreate());
21635             cfg.id = Roo.id();
21636             
21637             if (this.cls) {
21638                 cfg.cls += ' ' + this.cls;
21639             }
21640             if (this.style) {
21641                 cfg.style = this.style;
21642             }
21643             //Roo.log("adding to ");
21644             this.el = Roo.get(document.body).createChild(cfg, position);
21645 //            Roo.log(this.el);
21646         }
21647         
21648         this.contentEl = this.el.select('.popover-content',true).first();
21649         this.headerEl =  this.el.select('.popover-title',true).first();
21650         
21651         var nitems = [];
21652         if(typeof(this.items) != 'undefined'){
21653             var items = this.items;
21654             delete this.items;
21655
21656             for(var i =0;i < items.length;i++) {
21657                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21658             }
21659         }
21660
21661         this.items = nitems;
21662         
21663         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21664         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21665         
21666         
21667         
21668         this.initEvents();
21669     },
21670     
21671     resizeMask : function()
21672     {
21673         this.maskEl.setSize(
21674             Roo.lib.Dom.getViewWidth(true),
21675             Roo.lib.Dom.getViewHeight(true)
21676         );
21677     },
21678     
21679     initEvents : function()
21680     {
21681         
21682         if (!this.modal) { 
21683             Roo.bootstrap.Popover.register(this);
21684         }
21685          
21686         this.arrowEl = this.el.select('.arrow',true).first();
21687         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21688         this.el.enableDisplayMode('block');
21689         this.el.hide();
21690  
21691         
21692         if (this.over === false && !this.parent()) {
21693             return; 
21694         }
21695         if (this.triggers === false) {
21696             return;
21697         }
21698          
21699         // support parent
21700         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21701         var triggers = this.trigger ? this.trigger.split(' ') : [];
21702         Roo.each(triggers, function(trigger) {
21703         
21704             if (trigger == 'click') {
21705                 on_el.on('click', this.toggle, this);
21706             } else if (trigger != 'manual') {
21707                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21708                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21709       
21710                 on_el.on(eventIn  ,this.enter, this);
21711                 on_el.on(eventOut, this.leave, this);
21712             }
21713         }, this);
21714     },
21715     
21716     
21717     // private
21718     timeout : null,
21719     hoverState : null,
21720     
21721     toggle : function () {
21722         this.hoverState == 'in' ? this.leave() : this.enter();
21723     },
21724     
21725     enter : function () {
21726         
21727         clearTimeout(this.timeout);
21728     
21729         this.hoverState = 'in';
21730     
21731         if (!this.delay || !this.delay.show) {
21732             this.show();
21733             return;
21734         }
21735         var _t = this;
21736         this.timeout = setTimeout(function () {
21737             if (_t.hoverState == 'in') {
21738                 _t.show();
21739             }
21740         }, this.delay.show)
21741     },
21742     
21743     leave : function() {
21744         clearTimeout(this.timeout);
21745     
21746         this.hoverState = 'out';
21747     
21748         if (!this.delay || !this.delay.hide) {
21749             this.hide();
21750             return;
21751         }
21752         var _t = this;
21753         this.timeout = setTimeout(function () {
21754             if (_t.hoverState == 'out') {
21755                 _t.hide();
21756             }
21757         }, this.delay.hide)
21758     },
21759     /**
21760      * Show the popover
21761      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21762      * @param {string} (left|right|top|bottom) position
21763      */
21764     show : function (on_el, placement)
21765     {
21766         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21767         on_el = on_el || false; // default to false
21768          
21769         if (!on_el) {
21770             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21771                 on_el = this.parent().el;
21772             } else if (this.over) {
21773                 on_el = Roo.get(this.over);
21774             }
21775             
21776         }
21777         
21778         this.alignEl = Roo.get( on_el );
21779
21780         if (!this.el) {
21781             this.render(document.body);
21782         }
21783         
21784         
21785          
21786         
21787         if (this.title === false) {
21788             this.headerEl.hide();
21789         }
21790         
21791        
21792         this.el.show();
21793         this.el.dom.style.display = 'block';
21794          
21795  
21796         if (this.alignEl) {
21797             this.updatePosition(this.placement, true);
21798              
21799         } else {
21800             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21801             var es = this.el.getSize();
21802             var x = Roo.lib.Dom.getViewWidth()/2;
21803             var y = Roo.lib.Dom.getViewHeight()/2;
21804             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21805             
21806         }
21807
21808         
21809         //var arrow = this.el.select('.arrow',true).first();
21810         //arrow.set(align[2], 
21811         
21812         this.el.addClass('in');
21813         
21814          
21815         
21816         this.hoverState = 'in';
21817         
21818         if (this.modal) {
21819             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21820             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21821             this.maskEl.dom.style.display = 'block';
21822             this.maskEl.addClass('show');
21823         }
21824         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21825  
21826         this.fireEvent('show', this);
21827         
21828     },
21829     /**
21830      * fire this manually after loading a grid in the table for example
21831      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21832      * @param {Boolean} try and move it if we cant get right position.
21833      */
21834     updatePosition : function(placement, try_move)
21835     {
21836         // allow for calling with no parameters
21837         placement = placement   ? placement :  this.placement;
21838         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21839         
21840         this.el.removeClass([
21841             'fade','top','bottom', 'left', 'right','in',
21842             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21843         ]);
21844         this.el.addClass(placement + ' bs-popover-' + placement);
21845         
21846         if (!this.alignEl ) {
21847             return false;
21848         }
21849         
21850         switch (placement) {
21851             case 'right':
21852                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21853                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21854                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21855                     //normal display... or moved up/down.
21856                     this.el.setXY(offset);
21857                     var xy = this.alignEl.getAnchorXY('tr', false);
21858                     xy[0]+=2;xy[1]+=5;
21859                     this.arrowEl.setXY(xy);
21860                     return true;
21861                 }
21862                 // continue through...
21863                 return this.updatePosition('left', false);
21864                 
21865             
21866             case 'left':
21867                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21868                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21869                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21870                     //normal display... or moved up/down.
21871                     this.el.setXY(offset);
21872                     var xy = this.alignEl.getAnchorXY('tl', false);
21873                     xy[0]-=10;xy[1]+=5; // << fix me
21874                     this.arrowEl.setXY(xy);
21875                     return true;
21876                 }
21877                 // call self...
21878                 return this.updatePosition('right', false);
21879             
21880             case 'top':
21881                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21882                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21883                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21884                     //normal display... or moved up/down.
21885                     this.el.setXY(offset);
21886                     var xy = this.alignEl.getAnchorXY('t', false);
21887                     xy[1]-=10; // << fix me
21888                     this.arrowEl.setXY(xy);
21889                     return true;
21890                 }
21891                 // fall through
21892                return this.updatePosition('bottom', false);
21893             
21894             case 'bottom':
21895                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21896                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21897                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21898                     //normal display... or moved up/down.
21899                     this.el.setXY(offset);
21900                     var xy = this.alignEl.getAnchorXY('b', false);
21901                      xy[1]+=2; // << fix me
21902                     this.arrowEl.setXY(xy);
21903                     return true;
21904                 }
21905                 // fall through
21906                 return this.updatePosition('top', false);
21907                 
21908             
21909         }
21910         
21911         
21912         return false;
21913     },
21914     
21915     hide : function()
21916     {
21917         this.el.setXY([0,0]);
21918         this.el.removeClass('in');
21919         this.el.hide();
21920         this.hoverState = null;
21921         this.maskEl.hide(); // always..
21922         this.fireEvent('hide', this);
21923     }
21924     
21925 });
21926
21927
21928 Roo.apply(Roo.bootstrap.Popover, {
21929
21930     alignment : {
21931         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21932         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21933         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21934         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21935     },
21936     
21937     zIndex : 20001,
21938
21939     clickHander : false,
21940     
21941     
21942
21943     onMouseDown : function(e)
21944     {
21945         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21946             /// what is nothing is showing..
21947             this.hideAll();
21948         }
21949          
21950     },
21951     
21952     
21953     popups : [],
21954     
21955     register : function(popup)
21956     {
21957         if (!Roo.bootstrap.Popover.clickHandler) {
21958             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21959         }
21960         // hide other popups.
21961         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21962         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21963         this.hideAll(); //<< why?
21964         //this.popups.push(popup);
21965     },
21966     hideAll : function()
21967     {
21968         this.popups.forEach(function(p) {
21969             p.hide();
21970         });
21971     },
21972     onShow : function() {
21973         Roo.bootstrap.Popover.popups.push(this);
21974     },
21975     onHide : function() {
21976         Roo.bootstrap.Popover.popups.remove(this);
21977     } 
21978
21979 });/*
21980  * - LGPL
21981  *
21982  * Card header - holder for the card header elements.
21983  * 
21984  */
21985
21986 /**
21987  * @class Roo.bootstrap.PopoverNav
21988  * @extends Roo.bootstrap.nav.Simplebar
21989  * @parent Roo.bootstrap.Popover
21990  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21991  * Bootstrap Popover header navigation class
21992  * FIXME? should this go under nav?
21993  *
21994  * 
21995  * @constructor
21996  * Create a new Popover Header Navigation 
21997  * @param {Object} config The config object
21998  */
21999
22000 Roo.bootstrap.PopoverNav = function(config){
22001     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22002 };
22003
22004 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22005     
22006     
22007     container_method : 'getPopoverHeader' 
22008     
22009      
22010     
22011     
22012    
22013 });
22014
22015  
22016
22017  /*
22018  * - LGPL
22019  *
22020  * Progress
22021  * 
22022  */
22023
22024 /**
22025  * @class Roo.bootstrap.Progress
22026  * @extends Roo.bootstrap.Component
22027  * @children Roo.bootstrap.ProgressBar
22028  * Bootstrap Progress class
22029  * @cfg {Boolean} striped striped of the progress bar
22030  * @cfg {Boolean} active animated of the progress bar
22031  * 
22032  * 
22033  * @constructor
22034  * Create a new Progress
22035  * @param {Object} config The config object
22036  */
22037
22038 Roo.bootstrap.Progress = function(config){
22039     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22040 };
22041
22042 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22043     
22044     striped : false,
22045     active: false,
22046     
22047     getAutoCreate : function(){
22048         var cfg = {
22049             tag: 'div',
22050             cls: 'progress'
22051         };
22052         
22053         
22054         if(this.striped){
22055             cfg.cls += ' progress-striped';
22056         }
22057       
22058         if(this.active){
22059             cfg.cls += ' active';
22060         }
22061         
22062         
22063         return cfg;
22064     }
22065    
22066 });
22067
22068  
22069
22070  /*
22071  * - LGPL
22072  *
22073  * ProgressBar
22074  * 
22075  */
22076
22077 /**
22078  * @class Roo.bootstrap.ProgressBar
22079  * @extends Roo.bootstrap.Component
22080  * Bootstrap ProgressBar class
22081  * @cfg {Number} aria_valuenow aria-value now
22082  * @cfg {Number} aria_valuemin aria-value min
22083  * @cfg {Number} aria_valuemax aria-value max
22084  * @cfg {String} label label for the progress bar
22085  * @cfg {String} panel (success | info | warning | danger )
22086  * @cfg {String} role role of the progress bar
22087  * @cfg {String} sr_only text
22088  * 
22089  * 
22090  * @constructor
22091  * Create a new ProgressBar
22092  * @param {Object} config The config object
22093  */
22094
22095 Roo.bootstrap.ProgressBar = function(config){
22096     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22097 };
22098
22099 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22100     
22101     aria_valuenow : 0,
22102     aria_valuemin : 0,
22103     aria_valuemax : 100,
22104     label : false,
22105     panel : false,
22106     role : false,
22107     sr_only: false,
22108     
22109     getAutoCreate : function()
22110     {
22111         
22112         var cfg = {
22113             tag: 'div',
22114             cls: 'progress-bar',
22115             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22116         };
22117         
22118         if(this.sr_only){
22119             cfg.cn = {
22120                 tag: 'span',
22121                 cls: 'sr-only',
22122                 html: this.sr_only
22123             }
22124         }
22125         
22126         if(this.role){
22127             cfg.role = this.role;
22128         }
22129         
22130         if(this.aria_valuenow){
22131             cfg['aria-valuenow'] = this.aria_valuenow;
22132         }
22133         
22134         if(this.aria_valuemin){
22135             cfg['aria-valuemin'] = this.aria_valuemin;
22136         }
22137         
22138         if(this.aria_valuemax){
22139             cfg['aria-valuemax'] = this.aria_valuemax;
22140         }
22141         
22142         if(this.label && !this.sr_only){
22143             cfg.html = this.label;
22144         }
22145         
22146         if(this.panel){
22147             cfg.cls += ' progress-bar-' + this.panel;
22148         }
22149         
22150         return cfg;
22151     },
22152     
22153     update : function(aria_valuenow)
22154     {
22155         this.aria_valuenow = aria_valuenow;
22156         
22157         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22158     }
22159    
22160 });
22161
22162  
22163
22164  /**
22165  * @class Roo.bootstrap.TabGroup
22166  * @extends Roo.bootstrap.Column
22167  * @children Roo.bootstrap.TabPanel
22168  * Bootstrap Column class
22169  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22170  * @cfg {Boolean} carousel true to make the group behave like a carousel
22171  * @cfg {Boolean} bullets show bullets for the panels
22172  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22173  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22174  * @cfg {Boolean} showarrow (true|false) show arrow default true
22175  * 
22176  * @constructor
22177  * Create a new TabGroup
22178  * @param {Object} config The config object
22179  */
22180
22181 Roo.bootstrap.TabGroup = function(config){
22182     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22183     if (!this.navId) {
22184         this.navId = Roo.id();
22185     }
22186     this.tabs = [];
22187     Roo.bootstrap.TabGroup.register(this);
22188     
22189 };
22190
22191 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22192     
22193     carousel : false,
22194     transition : false,
22195     bullets : 0,
22196     timer : 0,
22197     autoslide : false,
22198     slideFn : false,
22199     slideOnTouch : false,
22200     showarrow : true,
22201     
22202     getAutoCreate : function()
22203     {
22204         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22205         
22206         cfg.cls += ' tab-content';
22207         
22208         if (this.carousel) {
22209             cfg.cls += ' carousel slide';
22210             
22211             cfg.cn = [{
22212                cls : 'carousel-inner',
22213                cn : []
22214             }];
22215         
22216             if(this.bullets  && !Roo.isTouch){
22217                 
22218                 var bullets = {
22219                     cls : 'carousel-bullets',
22220                     cn : []
22221                 };
22222                
22223                 if(this.bullets_cls){
22224                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22225                 }
22226                 
22227                 bullets.cn.push({
22228                     cls : 'clear'
22229                 });
22230                 
22231                 cfg.cn[0].cn.push(bullets);
22232             }
22233             
22234             if(this.showarrow){
22235                 cfg.cn[0].cn.push({
22236                     tag : 'div',
22237                     class : 'carousel-arrow',
22238                     cn : [
22239                         {
22240                             tag : 'div',
22241                             class : 'carousel-prev',
22242                             cn : [
22243                                 {
22244                                     tag : 'i',
22245                                     class : 'fa fa-chevron-left'
22246                                 }
22247                             ]
22248                         },
22249                         {
22250                             tag : 'div',
22251                             class : 'carousel-next',
22252                             cn : [
22253                                 {
22254                                     tag : 'i',
22255                                     class : 'fa fa-chevron-right'
22256                                 }
22257                             ]
22258                         }
22259                     ]
22260                 });
22261             }
22262             
22263         }
22264         
22265         return cfg;
22266     },
22267     
22268     initEvents:  function()
22269     {
22270 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22271 //            this.el.on("touchstart", this.onTouchStart, this);
22272 //        }
22273         
22274         if(this.autoslide){
22275             var _this = this;
22276             
22277             this.slideFn = window.setInterval(function() {
22278                 _this.showPanelNext();
22279             }, this.timer);
22280         }
22281         
22282         if(this.showarrow){
22283             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22284             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22285         }
22286         
22287         
22288     },
22289     
22290 //    onTouchStart : function(e, el, o)
22291 //    {
22292 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22293 //            return;
22294 //        }
22295 //        
22296 //        this.showPanelNext();
22297 //    },
22298     
22299     
22300     getChildContainer : function()
22301     {
22302         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22303     },
22304     
22305     /**
22306     * register a Navigation item
22307     * @param {Roo.bootstrap.nav.Item} the navitem to add
22308     */
22309     register : function(item)
22310     {
22311         this.tabs.push( item);
22312         item.navId = this.navId; // not really needed..
22313         this.addBullet();
22314     
22315     },
22316     
22317     getActivePanel : function()
22318     {
22319         var r = false;
22320         Roo.each(this.tabs, function(t) {
22321             if (t.active) {
22322                 r = t;
22323                 return false;
22324             }
22325             return null;
22326         });
22327         return r;
22328         
22329     },
22330     getPanelByName : function(n)
22331     {
22332         var r = false;
22333         Roo.each(this.tabs, function(t) {
22334             if (t.tabId == n) {
22335                 r = t;
22336                 return false;
22337             }
22338             return null;
22339         });
22340         return r;
22341     },
22342     indexOfPanel : function(p)
22343     {
22344         var r = false;
22345         Roo.each(this.tabs, function(t,i) {
22346             if (t.tabId == p.tabId) {
22347                 r = i;
22348                 return false;
22349             }
22350             return null;
22351         });
22352         return r;
22353     },
22354     /**
22355      * show a specific panel
22356      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22357      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22358      */
22359     showPanel : function (pan)
22360     {
22361         if(this.transition || typeof(pan) == 'undefined'){
22362             Roo.log("waiting for the transitionend");
22363             return false;
22364         }
22365         
22366         if (typeof(pan) == 'number') {
22367             pan = this.tabs[pan];
22368         }
22369         
22370         if (typeof(pan) == 'string') {
22371             pan = this.getPanelByName(pan);
22372         }
22373         
22374         var cur = this.getActivePanel();
22375         
22376         if(!pan || !cur){
22377             Roo.log('pan or acitve pan is undefined');
22378             return false;
22379         }
22380         
22381         if (pan.tabId == this.getActivePanel().tabId) {
22382             return true;
22383         }
22384         
22385         if (false === cur.fireEvent('beforedeactivate')) {
22386             return false;
22387         }
22388         
22389         if(this.bullets > 0 && !Roo.isTouch){
22390             this.setActiveBullet(this.indexOfPanel(pan));
22391         }
22392         
22393         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22394             
22395             //class="carousel-item carousel-item-next carousel-item-left"
22396             
22397             this.transition = true;
22398             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22399             var lr = dir == 'next' ? 'left' : 'right';
22400             pan.el.addClass(dir); // or prev
22401             pan.el.addClass('carousel-item-' + dir); // or prev
22402             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22403             cur.el.addClass(lr); // or right
22404             pan.el.addClass(lr);
22405             cur.el.addClass('carousel-item-' +lr); // or right
22406             pan.el.addClass('carousel-item-' +lr);
22407             
22408             
22409             var _this = this;
22410             cur.el.on('transitionend', function() {
22411                 Roo.log("trans end?");
22412                 
22413                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22414                 pan.setActive(true);
22415                 
22416                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22417                 cur.setActive(false);
22418                 
22419                 _this.transition = false;
22420                 
22421             }, this, { single:  true } );
22422             
22423             return true;
22424         }
22425         
22426         cur.setActive(false);
22427         pan.setActive(true);
22428         
22429         return true;
22430         
22431     },
22432     showPanelNext : function()
22433     {
22434         var i = this.indexOfPanel(this.getActivePanel());
22435         
22436         if (i >= this.tabs.length - 1 && !this.autoslide) {
22437             return;
22438         }
22439         
22440         if (i >= this.tabs.length - 1 && this.autoslide) {
22441             i = -1;
22442         }
22443         
22444         this.showPanel(this.tabs[i+1]);
22445     },
22446     
22447     showPanelPrev : function()
22448     {
22449         var i = this.indexOfPanel(this.getActivePanel());
22450         
22451         if (i  < 1 && !this.autoslide) {
22452             return;
22453         }
22454         
22455         if (i < 1 && this.autoslide) {
22456             i = this.tabs.length;
22457         }
22458         
22459         this.showPanel(this.tabs[i-1]);
22460     },
22461     
22462     
22463     addBullet: function()
22464     {
22465         if(!this.bullets || Roo.isTouch){
22466             return;
22467         }
22468         var ctr = this.el.select('.carousel-bullets',true).first();
22469         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22470         var bullet = ctr.createChild({
22471             cls : 'bullet bullet-' + i
22472         },ctr.dom.lastChild);
22473         
22474         
22475         var _this = this;
22476         
22477         bullet.on('click', (function(e, el, o, ii, t){
22478
22479             e.preventDefault();
22480
22481             this.showPanel(ii);
22482
22483             if(this.autoslide && this.slideFn){
22484                 clearInterval(this.slideFn);
22485                 this.slideFn = window.setInterval(function() {
22486                     _this.showPanelNext();
22487                 }, this.timer);
22488             }
22489
22490         }).createDelegate(this, [i, bullet], true));
22491                 
22492         
22493     },
22494      
22495     setActiveBullet : function(i)
22496     {
22497         if(Roo.isTouch){
22498             return;
22499         }
22500         
22501         Roo.each(this.el.select('.bullet', true).elements, function(el){
22502             el.removeClass('selected');
22503         });
22504
22505         var bullet = this.el.select('.bullet-' + i, true).first();
22506         
22507         if(!bullet){
22508             return;
22509         }
22510         
22511         bullet.addClass('selected');
22512     }
22513     
22514     
22515   
22516 });
22517
22518  
22519
22520  
22521  
22522 Roo.apply(Roo.bootstrap.TabGroup, {
22523     
22524     groups: {},
22525      /**
22526     * register a Navigation Group
22527     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22528     */
22529     register : function(navgrp)
22530     {
22531         this.groups[navgrp.navId] = navgrp;
22532         
22533     },
22534     /**
22535     * fetch a Navigation Group based on the navigation ID
22536     * if one does not exist , it will get created.
22537     * @param {string} the navgroup to add
22538     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22539     */
22540     get: function(navId) {
22541         if (typeof(this.groups[navId]) == 'undefined') {
22542             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22543         }
22544         return this.groups[navId] ;
22545     }
22546     
22547     
22548     
22549 });
22550
22551  /*
22552  * - LGPL
22553  *
22554  * TabPanel
22555  * 
22556  */
22557
22558 /**
22559  * @class Roo.bootstrap.TabPanel
22560  * @extends Roo.bootstrap.Component
22561  * @children Roo.bootstrap.Component
22562  * Bootstrap TabPanel class
22563  * @cfg {Boolean} active panel active
22564  * @cfg {String} html panel content
22565  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22566  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22567  * @cfg {String} href click to link..
22568  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22569  * 
22570  * 
22571  * @constructor
22572  * Create a new TabPanel
22573  * @param {Object} config The config object
22574  */
22575
22576 Roo.bootstrap.TabPanel = function(config){
22577     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22578     this.addEvents({
22579         /**
22580              * @event changed
22581              * Fires when the active status changes
22582              * @param {Roo.bootstrap.TabPanel} this
22583              * @param {Boolean} state the new state
22584             
22585          */
22586         'changed': true,
22587         /**
22588              * @event beforedeactivate
22589              * Fires before a tab is de-activated - can be used to do validation on a form.
22590              * @param {Roo.bootstrap.TabPanel} this
22591              * @return {Boolean} false if there is an error
22592             
22593          */
22594         'beforedeactivate': true
22595      });
22596     
22597     this.tabId = this.tabId || Roo.id();
22598   
22599 };
22600
22601 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22602     
22603     active: false,
22604     html: false,
22605     tabId: false,
22606     navId : false,
22607     href : '',
22608     touchSlide : false,
22609     getAutoCreate : function(){
22610         
22611         
22612         var cfg = {
22613             tag: 'div',
22614             // item is needed for carousel - not sure if it has any effect otherwise
22615             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22616             html: this.html || ''
22617         };
22618         
22619         if(this.active){
22620             cfg.cls += ' active';
22621         }
22622         
22623         if(this.tabId){
22624             cfg.tabId = this.tabId;
22625         }
22626         
22627         
22628         
22629         return cfg;
22630     },
22631     
22632     initEvents:  function()
22633     {
22634         var p = this.parent();
22635         
22636         this.navId = this.navId || p.navId;
22637         
22638         if (typeof(this.navId) != 'undefined') {
22639             // not really needed.. but just in case.. parent should be a NavGroup.
22640             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22641             
22642             tg.register(this);
22643             
22644             var i = tg.tabs.length - 1;
22645             
22646             if(this.active && tg.bullets > 0 && i < tg.bullets){
22647                 tg.setActiveBullet(i);
22648             }
22649         }
22650         
22651         this.el.on('click', this.onClick, this);
22652         
22653         if(Roo.isTouch && this.touchSlide){
22654             this.el.on("touchstart", this.onTouchStart, this);
22655             this.el.on("touchmove", this.onTouchMove, this);
22656             this.el.on("touchend", this.onTouchEnd, this);
22657         }
22658         
22659     },
22660     
22661     onRender : function(ct, position)
22662     {
22663         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22664     },
22665     
22666     setActive : function(state)
22667     {
22668         Roo.log("panel - set active " + this.tabId + "=" + state);
22669         
22670         this.active = state;
22671         if (!state) {
22672             this.el.removeClass('active');
22673             
22674         } else  if (!this.el.hasClass('active')) {
22675             this.el.addClass('active');
22676         }
22677         
22678         this.fireEvent('changed', this, state);
22679     },
22680     
22681     onClick : function(e)
22682     {
22683         e.preventDefault();
22684         
22685         if(!this.href.length){
22686             return;
22687         }
22688         
22689         window.location.href = this.href;
22690     },
22691     
22692     startX : 0,
22693     startY : 0,
22694     endX : 0,
22695     endY : 0,
22696     swiping : false,
22697     
22698     onTouchStart : function(e)
22699     {
22700         this.swiping = false;
22701         
22702         this.startX = e.browserEvent.touches[0].clientX;
22703         this.startY = e.browserEvent.touches[0].clientY;
22704     },
22705     
22706     onTouchMove : function(e)
22707     {
22708         this.swiping = true;
22709         
22710         this.endX = e.browserEvent.touches[0].clientX;
22711         this.endY = e.browserEvent.touches[0].clientY;
22712     },
22713     
22714     onTouchEnd : function(e)
22715     {
22716         if(!this.swiping){
22717             this.onClick(e);
22718             return;
22719         }
22720         
22721         var tabGroup = this.parent();
22722         
22723         if(this.endX > this.startX){ // swiping right
22724             tabGroup.showPanelPrev();
22725             return;
22726         }
22727         
22728         if(this.startX > this.endX){ // swiping left
22729             tabGroup.showPanelNext();
22730             return;
22731         }
22732     }
22733     
22734     
22735 });
22736  
22737
22738  
22739
22740  /*
22741  * - LGPL
22742  *
22743  * DateField
22744  * 
22745  */
22746
22747 /**
22748  * @class Roo.bootstrap.DateField
22749  * @extends Roo.bootstrap.Input
22750  * Bootstrap DateField class
22751  * @cfg {Number} weekStart default 0
22752  * @cfg {String} viewMode default empty, (months|years)
22753  * @cfg {String} minViewMode default empty, (months|years)
22754  * @cfg {Number} startDate default -Infinity
22755  * @cfg {Number} endDate default Infinity
22756  * @cfg {Boolean} todayHighlight default false
22757  * @cfg {Boolean} todayBtn default false
22758  * @cfg {Boolean} calendarWeeks default false
22759  * @cfg {Object} daysOfWeekDisabled default empty
22760  * @cfg {Boolean} singleMode default false (true | false)
22761  * 
22762  * @cfg {Boolean} keyboardNavigation default true
22763  * @cfg {String} language default en
22764  * 
22765  * @constructor
22766  * Create a new DateField
22767  * @param {Object} config The config object
22768  */
22769
22770 Roo.bootstrap.DateField = function(config){
22771     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22772      this.addEvents({
22773             /**
22774              * @event show
22775              * Fires when this field show.
22776              * @param {Roo.bootstrap.DateField} this
22777              * @param {Mixed} date The date value
22778              */
22779             show : true,
22780             /**
22781              * @event show
22782              * Fires when this field hide.
22783              * @param {Roo.bootstrap.DateField} this
22784              * @param {Mixed} date The date value
22785              */
22786             hide : true,
22787             /**
22788              * @event select
22789              * Fires when select a date.
22790              * @param {Roo.bootstrap.DateField} this
22791              * @param {Mixed} date The date value
22792              */
22793             select : true,
22794             /**
22795              * @event beforeselect
22796              * Fires when before select a date.
22797              * @param {Roo.bootstrap.DateField} this
22798              * @param {Mixed} date The date value
22799              */
22800             beforeselect : true
22801         });
22802 };
22803
22804 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22805     
22806     /**
22807      * @cfg {String} format
22808      * The default date format string which can be overriden for localization support.  The format must be
22809      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22810      */
22811     format : "m/d/y",
22812     /**
22813      * @cfg {String} altFormats
22814      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22815      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22816      */
22817     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22818     
22819     weekStart : 0,
22820     
22821     viewMode : '',
22822     
22823     minViewMode : '',
22824     
22825     todayHighlight : false,
22826     
22827     todayBtn: false,
22828     
22829     language: 'en',
22830     
22831     keyboardNavigation: true,
22832     
22833     calendarWeeks: false,
22834     
22835     startDate: -Infinity,
22836     
22837     endDate: Infinity,
22838     
22839     daysOfWeekDisabled: [],
22840     
22841     _events: [],
22842     
22843     singleMode : false,
22844     
22845     UTCDate: function()
22846     {
22847         return new Date(Date.UTC.apply(Date, arguments));
22848     },
22849     
22850     UTCToday: function()
22851     {
22852         var today = new Date();
22853         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22854     },
22855     
22856     getDate: function() {
22857             var d = this.getUTCDate();
22858             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22859     },
22860     
22861     getUTCDate: function() {
22862             return this.date;
22863     },
22864     
22865     setDate: function(d) {
22866             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22867     },
22868     
22869     setUTCDate: function(d) {
22870             this.date = d;
22871             this.setValue(this.formatDate(this.date));
22872     },
22873         
22874     onRender: function(ct, position)
22875     {
22876         
22877         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22878         
22879         this.language = this.language || 'en';
22880         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22881         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22882         
22883         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22884         this.format = this.format || 'm/d/y';
22885         this.isInline = false;
22886         this.isInput = true;
22887         this.component = this.el.select('.add-on', true).first() || false;
22888         this.component = (this.component && this.component.length === 0) ? false : this.component;
22889         this.hasInput = this.component && this.inputEl().length;
22890         
22891         if (typeof(this.minViewMode === 'string')) {
22892             switch (this.minViewMode) {
22893                 case 'months':
22894                     this.minViewMode = 1;
22895                     break;
22896                 case 'years':
22897                     this.minViewMode = 2;
22898                     break;
22899                 default:
22900                     this.minViewMode = 0;
22901                     break;
22902             }
22903         }
22904         
22905         if (typeof(this.viewMode === 'string')) {
22906             switch (this.viewMode) {
22907                 case 'months':
22908                     this.viewMode = 1;
22909                     break;
22910                 case 'years':
22911                     this.viewMode = 2;
22912                     break;
22913                 default:
22914                     this.viewMode = 0;
22915                     break;
22916             }
22917         }
22918                 
22919         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22920         
22921 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22922         
22923         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22924         
22925         this.picker().on('mousedown', this.onMousedown, this);
22926         this.picker().on('click', this.onClick, this);
22927         
22928         this.picker().addClass('datepicker-dropdown');
22929         
22930         this.startViewMode = this.viewMode;
22931         
22932         if(this.singleMode){
22933             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22934                 v.setVisibilityMode(Roo.Element.DISPLAY);
22935                 v.hide();
22936             });
22937             
22938             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22939                 v.setStyle('width', '189px');
22940             });
22941         }
22942         
22943         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22944             if(!this.calendarWeeks){
22945                 v.remove();
22946                 return;
22947             }
22948             
22949             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22950             v.attr('colspan', function(i, val){
22951                 return parseInt(val) + 1;
22952             });
22953         });
22954                         
22955         
22956         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22957         
22958         this.setStartDate(this.startDate);
22959         this.setEndDate(this.endDate);
22960         
22961         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22962         
22963         this.fillDow();
22964         this.fillMonths();
22965         this.update();
22966         this.showMode();
22967         
22968         if(this.isInline) {
22969             this.showPopup();
22970         }
22971     },
22972     
22973     picker : function()
22974     {
22975         return this.pickerEl;
22976 //        return this.el.select('.datepicker', true).first();
22977     },
22978     
22979     fillDow: function()
22980     {
22981         var dowCnt = this.weekStart;
22982         
22983         var dow = {
22984             tag: 'tr',
22985             cn: [
22986                 
22987             ]
22988         };
22989         
22990         if(this.calendarWeeks){
22991             dow.cn.push({
22992                 tag: 'th',
22993                 cls: 'cw',
22994                 html: '&nbsp;'
22995             })
22996         }
22997         
22998         while (dowCnt < this.weekStart + 7) {
22999             dow.cn.push({
23000                 tag: 'th',
23001                 cls: 'dow',
23002                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23003             });
23004         }
23005         
23006         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23007     },
23008     
23009     fillMonths: function()
23010     {    
23011         var i = 0;
23012         var months = this.picker().select('>.datepicker-months td', true).first();
23013         
23014         months.dom.innerHTML = '';
23015         
23016         while (i < 12) {
23017             var month = {
23018                 tag: 'span',
23019                 cls: 'month',
23020                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
23021             };
23022             
23023             months.createChild(month);
23024         }
23025         
23026     },
23027     
23028     update: function()
23029     {
23030         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;
23031         
23032         if (this.date < this.startDate) {
23033             this.viewDate = new Date(this.startDate);
23034         } else if (this.date > this.endDate) {
23035             this.viewDate = new Date(this.endDate);
23036         } else {
23037             this.viewDate = new Date(this.date);
23038         }
23039         
23040         this.fill();
23041     },
23042     
23043     fill: function() 
23044     {
23045         var d = new Date(this.viewDate),
23046                 year = d.getUTCFullYear(),
23047                 month = d.getUTCMonth(),
23048                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23049                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23050                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23051                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23052                 currentDate = this.date && this.date.valueOf(),
23053                 today = this.UTCToday();
23054         
23055         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
23056         
23057 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
23058         
23059 //        this.picker.select('>tfoot th.today').
23060 //                                              .text(dates[this.language].today)
23061 //                                              .toggle(this.todayBtn !== false);
23062     
23063         this.updateNavArrows();
23064         this.fillMonths();
23065                                                 
23066         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23067         
23068         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23069          
23070         prevMonth.setUTCDate(day);
23071         
23072         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23073         
23074         var nextMonth = new Date(prevMonth);
23075         
23076         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23077         
23078         nextMonth = nextMonth.valueOf();
23079         
23080         var fillMonths = false;
23081         
23082         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23083         
23084         while(prevMonth.valueOf() <= nextMonth) {
23085             var clsName = '';
23086             
23087             if (prevMonth.getUTCDay() === this.weekStart) {
23088                 if(fillMonths){
23089                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23090                 }
23091                     
23092                 fillMonths = {
23093                     tag: 'tr',
23094                     cn: []
23095                 };
23096                 
23097                 if(this.calendarWeeks){
23098                     // ISO 8601: First week contains first thursday.
23099                     // ISO also states week starts on Monday, but we can be more abstract here.
23100                     var
23101                     // Start of current week: based on weekstart/current date
23102                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23103                     // Thursday of this week
23104                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23105                     // First Thursday of year, year from thursday
23106                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23107                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23108                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23109                     
23110                     fillMonths.cn.push({
23111                         tag: 'td',
23112                         cls: 'cw',
23113                         html: calWeek
23114                     });
23115                 }
23116             }
23117             
23118             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23119                 clsName += ' old';
23120             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23121                 clsName += ' new';
23122             }
23123             if (this.todayHighlight &&
23124                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23125                 prevMonth.getUTCMonth() == today.getMonth() &&
23126                 prevMonth.getUTCDate() == today.getDate()) {
23127                 clsName += ' today';
23128             }
23129             
23130             if (currentDate && prevMonth.valueOf() === currentDate) {
23131                 clsName += ' active';
23132             }
23133             
23134             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23135                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23136                     clsName += ' disabled';
23137             }
23138             
23139             fillMonths.cn.push({
23140                 tag: 'td',
23141                 cls: 'day ' + clsName,
23142                 html: prevMonth.getDate()
23143             });
23144             
23145             prevMonth.setDate(prevMonth.getDate()+1);
23146         }
23147           
23148         var currentYear = this.date && this.date.getUTCFullYear();
23149         var currentMonth = this.date && this.date.getUTCMonth();
23150         
23151         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23152         
23153         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23154             v.removeClass('active');
23155             
23156             if(currentYear === year && k === currentMonth){
23157                 v.addClass('active');
23158             }
23159             
23160             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23161                 v.addClass('disabled');
23162             }
23163             
23164         });
23165         
23166         
23167         year = parseInt(year/10, 10) * 10;
23168         
23169         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23170         
23171         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23172         
23173         year -= 1;
23174         for (var i = -1; i < 11; i++) {
23175             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23176                 tag: 'span',
23177                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23178                 html: year
23179             });
23180             
23181             year += 1;
23182         }
23183     },
23184     
23185     showMode: function(dir) 
23186     {
23187         if (dir) {
23188             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23189         }
23190         
23191         Roo.each(this.picker().select('>div',true).elements, function(v){
23192             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23193             v.hide();
23194         });
23195         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
23196     },
23197     
23198     place: function()
23199     {
23200         if(this.isInline) {
23201             return;
23202         }
23203         
23204         this.picker().removeClass(['bottom', 'top']);
23205         
23206         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23207             /*
23208              * place to the top of element!
23209              *
23210              */
23211             
23212             this.picker().addClass('top');
23213             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23214             
23215             return;
23216         }
23217         
23218         this.picker().addClass('bottom');
23219         
23220         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23221     },
23222     
23223     parseDate : function(value)
23224     {
23225         if(!value || value instanceof Date){
23226             return value;
23227         }
23228         var v = Date.parseDate(value, this.format);
23229         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23230             v = Date.parseDate(value, 'Y-m-d');
23231         }
23232         if(!v && this.altFormats){
23233             if(!this.altFormatsArray){
23234                 this.altFormatsArray = this.altFormats.split("|");
23235             }
23236             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23237                 v = Date.parseDate(value, this.altFormatsArray[i]);
23238             }
23239         }
23240         return v;
23241     },
23242     
23243     formatDate : function(date, fmt)
23244     {   
23245         return (!date || !(date instanceof Date)) ?
23246         date : date.dateFormat(fmt || this.format);
23247     },
23248     
23249     onFocus : function()
23250     {
23251         Roo.bootstrap.DateField.superclass.onFocus.call(this);
23252         this.showPopup();
23253     },
23254     
23255     onBlur : function()
23256     {
23257         Roo.bootstrap.DateField.superclass.onBlur.call(this);
23258         
23259         var d = this.inputEl().getValue();
23260         
23261         this.setValue(d);
23262                 
23263         this.hidePopup();
23264     },
23265     
23266     showPopup : function()
23267     {
23268         this.picker().show();
23269         this.update();
23270         this.place();
23271         
23272         this.fireEvent('showpopup', this, this.date);
23273     },
23274     
23275     hidePopup : function()
23276     {
23277         if(this.isInline) {
23278             return;
23279         }
23280         this.picker().hide();
23281         this.viewMode = this.startViewMode;
23282         this.showMode();
23283         
23284         this.fireEvent('hidepopup', this, this.date);
23285         
23286     },
23287     
23288     onMousedown: function(e)
23289     {
23290         e.stopPropagation();
23291         e.preventDefault();
23292     },
23293     
23294     keyup: function(e)
23295     {
23296         Roo.bootstrap.DateField.superclass.keyup.call(this);
23297         this.update();
23298     },
23299
23300     setValue: function(v)
23301     {
23302         if(this.fireEvent('beforeselect', this, v) !== false){
23303             var d = new Date(this.parseDate(v) ).clearTime();
23304         
23305             if(isNaN(d.getTime())){
23306                 this.date = this.viewDate = '';
23307                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23308                 return;
23309             }
23310
23311             v = this.formatDate(d);
23312
23313             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
23314
23315             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23316
23317             this.update();
23318
23319             this.fireEvent('select', this, this.date);
23320         }
23321     },
23322     
23323     getValue: function()
23324     {
23325         return this.formatDate(this.date);
23326     },
23327     
23328     fireKey: function(e)
23329     {
23330         if (!this.picker().isVisible()){
23331             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23332                 this.showPopup();
23333             }
23334             return;
23335         }
23336         
23337         var dateChanged = false,
23338         dir, day, month,
23339         newDate, newViewDate;
23340         
23341         switch(e.keyCode){
23342             case 27: // escape
23343                 this.hidePopup();
23344                 e.preventDefault();
23345                 break;
23346             case 37: // left
23347             case 39: // right
23348                 if (!this.keyboardNavigation) {
23349                     break;
23350                 }
23351                 dir = e.keyCode == 37 ? -1 : 1;
23352                 
23353                 if (e.ctrlKey){
23354                     newDate = this.moveYear(this.date, dir);
23355                     newViewDate = this.moveYear(this.viewDate, dir);
23356                 } else if (e.shiftKey){
23357                     newDate = this.moveMonth(this.date, dir);
23358                     newViewDate = this.moveMonth(this.viewDate, dir);
23359                 } else {
23360                     newDate = new Date(this.date);
23361                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23362                     newViewDate = new Date(this.viewDate);
23363                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23364                 }
23365                 if (this.dateWithinRange(newDate)){
23366                     this.date = newDate;
23367                     this.viewDate = newViewDate;
23368                     this.setValue(this.formatDate(this.date));
23369 //                    this.update();
23370                     e.preventDefault();
23371                     dateChanged = true;
23372                 }
23373                 break;
23374             case 38: // up
23375             case 40: // down
23376                 if (!this.keyboardNavigation) {
23377                     break;
23378                 }
23379                 dir = e.keyCode == 38 ? -1 : 1;
23380                 if (e.ctrlKey){
23381                     newDate = this.moveYear(this.date, dir);
23382                     newViewDate = this.moveYear(this.viewDate, dir);
23383                 } else if (e.shiftKey){
23384                     newDate = this.moveMonth(this.date, dir);
23385                     newViewDate = this.moveMonth(this.viewDate, dir);
23386                 } else {
23387                     newDate = new Date(this.date);
23388                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23389                     newViewDate = new Date(this.viewDate);
23390                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23391                 }
23392                 if (this.dateWithinRange(newDate)){
23393                     this.date = newDate;
23394                     this.viewDate = newViewDate;
23395                     this.setValue(this.formatDate(this.date));
23396 //                    this.update();
23397                     e.preventDefault();
23398                     dateChanged = true;
23399                 }
23400                 break;
23401             case 13: // enter
23402                 this.setValue(this.formatDate(this.date));
23403                 this.hidePopup();
23404                 e.preventDefault();
23405                 break;
23406             case 9: // tab
23407                 this.setValue(this.formatDate(this.date));
23408                 this.hidePopup();
23409                 break;
23410             case 16: // shift
23411             case 17: // ctrl
23412             case 18: // alt
23413                 break;
23414             default :
23415                 this.hidePopup();
23416                 
23417         }
23418     },
23419     
23420     
23421     onClick: function(e) 
23422     {
23423         e.stopPropagation();
23424         e.preventDefault();
23425         
23426         var target = e.getTarget();
23427         
23428         if(target.nodeName.toLowerCase() === 'i'){
23429             target = Roo.get(target).dom.parentNode;
23430         }
23431         
23432         var nodeName = target.nodeName;
23433         var className = target.className;
23434         var html = target.innerHTML;
23435         //Roo.log(nodeName);
23436         
23437         switch(nodeName.toLowerCase()) {
23438             case 'th':
23439                 switch(className) {
23440                     case 'switch':
23441                         this.showMode(1);
23442                         break;
23443                     case 'prev':
23444                     case 'next':
23445                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23446                         switch(this.viewMode){
23447                                 case 0:
23448                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23449                                         break;
23450                                 case 1:
23451                                 case 2:
23452                                         this.viewDate = this.moveYear(this.viewDate, dir);
23453                                         break;
23454                         }
23455                         this.fill();
23456                         break;
23457                     case 'today':
23458                         var date = new Date();
23459                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23460 //                        this.fill()
23461                         this.setValue(this.formatDate(this.date));
23462                         
23463                         this.hidePopup();
23464                         break;
23465                 }
23466                 break;
23467             case 'span':
23468                 if (className.indexOf('disabled') < 0) {
23469                 if (!this.viewDate) {
23470                     this.viewDate = new Date();
23471                 }
23472                 this.viewDate.setUTCDate(1);
23473                     if (className.indexOf('month') > -1) {
23474                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23475                     } else {
23476                         var year = parseInt(html, 10) || 0;
23477                         this.viewDate.setUTCFullYear(year);
23478                         
23479                     }
23480                     
23481                     if(this.singleMode){
23482                         this.setValue(this.formatDate(this.viewDate));
23483                         this.hidePopup();
23484                         return;
23485                     }
23486                     
23487                     this.showMode(-1);
23488                     this.fill();
23489                 }
23490                 break;
23491                 
23492             case 'td':
23493                 //Roo.log(className);
23494                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23495                     var day = parseInt(html, 10) || 1;
23496                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23497                         month = (this.viewDate || new Date()).getUTCMonth();
23498
23499                     if (className.indexOf('old') > -1) {
23500                         if(month === 0 ){
23501                             month = 11;
23502                             year -= 1;
23503                         }else{
23504                             month -= 1;
23505                         }
23506                     } else if (className.indexOf('new') > -1) {
23507                         if (month == 11) {
23508                             month = 0;
23509                             year += 1;
23510                         } else {
23511                             month += 1;
23512                         }
23513                     }
23514                     //Roo.log([year,month,day]);
23515                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23516                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23517 //                    this.fill();
23518                     //Roo.log(this.formatDate(this.date));
23519                     this.setValue(this.formatDate(this.date));
23520                     this.hidePopup();
23521                 }
23522                 break;
23523         }
23524     },
23525     
23526     setStartDate: function(startDate)
23527     {
23528         this.startDate = startDate || -Infinity;
23529         if (this.startDate !== -Infinity) {
23530             this.startDate = this.parseDate(this.startDate);
23531         }
23532         this.update();
23533         this.updateNavArrows();
23534     },
23535
23536     setEndDate: function(endDate)
23537     {
23538         this.endDate = endDate || Infinity;
23539         if (this.endDate !== Infinity) {
23540             this.endDate = this.parseDate(this.endDate);
23541         }
23542         this.update();
23543         this.updateNavArrows();
23544     },
23545     
23546     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23547     {
23548         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23549         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23550             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23551         }
23552         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23553             return parseInt(d, 10);
23554         });
23555         this.update();
23556         this.updateNavArrows();
23557     },
23558     
23559     updateNavArrows: function() 
23560     {
23561         if(this.singleMode){
23562             return;
23563         }
23564         
23565         var d = new Date(this.viewDate),
23566         year = d.getUTCFullYear(),
23567         month = d.getUTCMonth();
23568         
23569         Roo.each(this.picker().select('.prev', true).elements, function(v){
23570             v.show();
23571             switch (this.viewMode) {
23572                 case 0:
23573
23574                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23575                         v.hide();
23576                     }
23577                     break;
23578                 case 1:
23579                 case 2:
23580                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23581                         v.hide();
23582                     }
23583                     break;
23584             }
23585         });
23586         
23587         Roo.each(this.picker().select('.next', true).elements, function(v){
23588             v.show();
23589             switch (this.viewMode) {
23590                 case 0:
23591
23592                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23593                         v.hide();
23594                     }
23595                     break;
23596                 case 1:
23597                 case 2:
23598                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23599                         v.hide();
23600                     }
23601                     break;
23602             }
23603         })
23604     },
23605     
23606     moveMonth: function(date, dir)
23607     {
23608         if (!dir) {
23609             return date;
23610         }
23611         var new_date = new Date(date.valueOf()),
23612         day = new_date.getUTCDate(),
23613         month = new_date.getUTCMonth(),
23614         mag = Math.abs(dir),
23615         new_month, test;
23616         dir = dir > 0 ? 1 : -1;
23617         if (mag == 1){
23618             test = dir == -1
23619             // If going back one month, make sure month is not current month
23620             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23621             ? function(){
23622                 return new_date.getUTCMonth() == month;
23623             }
23624             // If going forward one month, make sure month is as expected
23625             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23626             : function(){
23627                 return new_date.getUTCMonth() != new_month;
23628             };
23629             new_month = month + dir;
23630             new_date.setUTCMonth(new_month);
23631             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23632             if (new_month < 0 || new_month > 11) {
23633                 new_month = (new_month + 12) % 12;
23634             }
23635         } else {
23636             // For magnitudes >1, move one month at a time...
23637             for (var i=0; i<mag; i++) {
23638                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23639                 new_date = this.moveMonth(new_date, dir);
23640             }
23641             // ...then reset the day, keeping it in the new month
23642             new_month = new_date.getUTCMonth();
23643             new_date.setUTCDate(day);
23644             test = function(){
23645                 return new_month != new_date.getUTCMonth();
23646             };
23647         }
23648         // Common date-resetting loop -- if date is beyond end of month, make it
23649         // end of month
23650         while (test()){
23651             new_date.setUTCDate(--day);
23652             new_date.setUTCMonth(new_month);
23653         }
23654         return new_date;
23655     },
23656
23657     moveYear: function(date, dir)
23658     {
23659         return this.moveMonth(date, dir*12);
23660     },
23661
23662     dateWithinRange: function(date)
23663     {
23664         return date >= this.startDate && date <= this.endDate;
23665     },
23666
23667     
23668     remove: function() 
23669     {
23670         this.picker().remove();
23671     },
23672     
23673     validateValue : function(value)
23674     {
23675         if(this.getVisibilityEl().hasClass('hidden')){
23676             return true;
23677         }
23678         
23679         if(value.length < 1)  {
23680             if(this.allowBlank){
23681                 return true;
23682             }
23683             return false;
23684         }
23685         
23686         if(value.length < this.minLength){
23687             return false;
23688         }
23689         if(value.length > this.maxLength){
23690             return false;
23691         }
23692         if(this.vtype){
23693             var vt = Roo.form.VTypes;
23694             if(!vt[this.vtype](value, this)){
23695                 return false;
23696             }
23697         }
23698         if(typeof this.validator == "function"){
23699             var msg = this.validator(value);
23700             if(msg !== true){
23701                 return false;
23702             }
23703         }
23704         
23705         if(this.regex && !this.regex.test(value)){
23706             return false;
23707         }
23708         
23709         if(typeof(this.parseDate(value)) == 'undefined'){
23710             return false;
23711         }
23712         
23713         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23714             return false;
23715         }      
23716         
23717         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23718             return false;
23719         } 
23720         
23721         
23722         return true;
23723     },
23724     
23725     reset : function()
23726     {
23727         this.date = this.viewDate = '';
23728         
23729         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23730     }
23731    
23732 });
23733
23734 Roo.apply(Roo.bootstrap.DateField,  {
23735     
23736     head : {
23737         tag: 'thead',
23738         cn: [
23739         {
23740             tag: 'tr',
23741             cn: [
23742             {
23743                 tag: 'th',
23744                 cls: 'prev',
23745                 html: '<i class="fa fa-arrow-left"/>'
23746             },
23747             {
23748                 tag: 'th',
23749                 cls: 'switch',
23750                 colspan: '5'
23751             },
23752             {
23753                 tag: 'th',
23754                 cls: 'next',
23755                 html: '<i class="fa fa-arrow-right"/>'
23756             }
23757
23758             ]
23759         }
23760         ]
23761     },
23762     
23763     content : {
23764         tag: 'tbody',
23765         cn: [
23766         {
23767             tag: 'tr',
23768             cn: [
23769             {
23770                 tag: 'td',
23771                 colspan: '7'
23772             }
23773             ]
23774         }
23775         ]
23776     },
23777     
23778     footer : {
23779         tag: 'tfoot',
23780         cn: [
23781         {
23782             tag: 'tr',
23783             cn: [
23784             {
23785                 tag: 'th',
23786                 colspan: '7',
23787                 cls: 'today'
23788             }
23789                     
23790             ]
23791         }
23792         ]
23793     },
23794     
23795     dates:{
23796         en: {
23797             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23798             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23799             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23800             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23801             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23802             today: "Today"
23803         }
23804     },
23805     
23806     modes: [
23807     {
23808         clsName: 'days',
23809         navFnc: 'Month',
23810         navStep: 1
23811     },
23812     {
23813         clsName: 'months',
23814         navFnc: 'FullYear',
23815         navStep: 1
23816     },
23817     {
23818         clsName: 'years',
23819         navFnc: 'FullYear',
23820         navStep: 10
23821     }]
23822 });
23823
23824 Roo.apply(Roo.bootstrap.DateField,  {
23825   
23826     template : {
23827         tag: 'div',
23828         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23829         cn: [
23830         {
23831             tag: 'div',
23832             cls: 'datepicker-days',
23833             cn: [
23834             {
23835                 tag: 'table',
23836                 cls: 'table-condensed',
23837                 cn:[
23838                 Roo.bootstrap.DateField.head,
23839                 {
23840                     tag: 'tbody'
23841                 },
23842                 Roo.bootstrap.DateField.footer
23843                 ]
23844             }
23845             ]
23846         },
23847         {
23848             tag: 'div',
23849             cls: 'datepicker-months',
23850             cn: [
23851             {
23852                 tag: 'table',
23853                 cls: 'table-condensed',
23854                 cn:[
23855                 Roo.bootstrap.DateField.head,
23856                 Roo.bootstrap.DateField.content,
23857                 Roo.bootstrap.DateField.footer
23858                 ]
23859             }
23860             ]
23861         },
23862         {
23863             tag: 'div',
23864             cls: 'datepicker-years',
23865             cn: [
23866             {
23867                 tag: 'table',
23868                 cls: 'table-condensed',
23869                 cn:[
23870                 Roo.bootstrap.DateField.head,
23871                 Roo.bootstrap.DateField.content,
23872                 Roo.bootstrap.DateField.footer
23873                 ]
23874             }
23875             ]
23876         }
23877         ]
23878     }
23879 });
23880
23881  
23882
23883  /*
23884  * - LGPL
23885  *
23886  * TimeField
23887  * 
23888  */
23889
23890 /**
23891  * @class Roo.bootstrap.TimeField
23892  * @extends Roo.bootstrap.Input
23893  * Bootstrap DateField class
23894  * 
23895  * 
23896  * @constructor
23897  * Create a new TimeField
23898  * @param {Object} config The config object
23899  */
23900
23901 Roo.bootstrap.TimeField = function(config){
23902     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23903     this.addEvents({
23904             /**
23905              * @event show
23906              * Fires when this field show.
23907              * @param {Roo.bootstrap.DateField} thisthis
23908              * @param {Mixed} date The date value
23909              */
23910             show : true,
23911             /**
23912              * @event show
23913              * Fires when this field hide.
23914              * @param {Roo.bootstrap.DateField} this
23915              * @param {Mixed} date The date value
23916              */
23917             hide : true,
23918             /**
23919              * @event select
23920              * Fires when select a date.
23921              * @param {Roo.bootstrap.DateField} this
23922              * @param {Mixed} date The date value
23923              */
23924             select : true
23925         });
23926 };
23927
23928 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23929     
23930     /**
23931      * @cfg {String} format
23932      * The default time format string which can be overriden for localization support.  The format must be
23933      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23934      */
23935     format : "H:i",
23936
23937     getAutoCreate : function()
23938     {
23939         this.after = '<i class="fa far fa-clock"></i>';
23940         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23941         
23942          
23943     },
23944     onRender: function(ct, position)
23945     {
23946         
23947         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23948                 
23949         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23950         
23951         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23952         
23953         this.pop = this.picker().select('>.datepicker-time',true).first();
23954         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23955         
23956         this.picker().on('mousedown', this.onMousedown, this);
23957         this.picker().on('click', this.onClick, this);
23958         
23959         this.picker().addClass('datepicker-dropdown');
23960     
23961         this.fillTime();
23962         this.update();
23963             
23964         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23965         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23966         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23967         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23968         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23969         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23970
23971     },
23972     
23973     fireKey: function(e){
23974         if (!this.picker().isVisible()){
23975             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23976                 this.show();
23977             }
23978             return;
23979         }
23980
23981         e.preventDefault();
23982         
23983         switch(e.keyCode){
23984             case 27: // escape
23985                 this.hide();
23986                 break;
23987             case 37: // left
23988             case 39: // right
23989                 this.onTogglePeriod();
23990                 break;
23991             case 38: // up
23992                 this.onIncrementMinutes();
23993                 break;
23994             case 40: // down
23995                 this.onDecrementMinutes();
23996                 break;
23997             case 13: // enter
23998             case 9: // tab
23999                 this.setTime();
24000                 break;
24001         }
24002     },
24003     
24004     onClick: function(e) {
24005         e.stopPropagation();
24006         e.preventDefault();
24007     },
24008     
24009     picker : function()
24010     {
24011         return this.pickerEl;
24012     },
24013     
24014     fillTime: function()
24015     {    
24016         var time = this.pop.select('tbody', true).first();
24017         
24018         time.dom.innerHTML = '';
24019         
24020         time.createChild({
24021             tag: 'tr',
24022             cn: [
24023                 {
24024                     tag: 'td',
24025                     cn: [
24026                         {
24027                             tag: 'a',
24028                             href: '#',
24029                             cls: 'btn',
24030                             cn: [
24031                                 {
24032                                     tag: 'i',
24033                                     cls: 'hours-up fa fas fa-chevron-up'
24034                                 }
24035                             ]
24036                         } 
24037                     ]
24038                 },
24039                 {
24040                     tag: 'td',
24041                     cls: 'separator'
24042                 },
24043                 {
24044                     tag: 'td',
24045                     cn: [
24046                         {
24047                             tag: 'a',
24048                             href: '#',
24049                             cls: 'btn',
24050                             cn: [
24051                                 {
24052                                     tag: 'i',
24053                                     cls: 'minutes-up fa fas fa-chevron-up'
24054                                 }
24055                             ]
24056                         }
24057                     ]
24058                 },
24059                 {
24060                     tag: 'td',
24061                     cls: 'separator'
24062                 }
24063             ]
24064         });
24065         
24066         time.createChild({
24067             tag: 'tr',
24068             cn: [
24069                 {
24070                     tag: 'td',
24071                     cn: [
24072                         {
24073                             tag: 'span',
24074                             cls: 'timepicker-hour',
24075                             html: '00'
24076                         }  
24077                     ]
24078                 },
24079                 {
24080                     tag: 'td',
24081                     cls: 'separator',
24082                     html: ':'
24083                 },
24084                 {
24085                     tag: 'td',
24086                     cn: [
24087                         {
24088                             tag: 'span',
24089                             cls: 'timepicker-minute',
24090                             html: '00'
24091                         }  
24092                     ]
24093                 },
24094                 {
24095                     tag: 'td',
24096                     cls: 'separator'
24097                 },
24098                 {
24099                     tag: 'td',
24100                     cn: [
24101                         {
24102                             tag: 'button',
24103                             type: 'button',
24104                             cls: 'btn btn-primary period',
24105                             html: 'AM'
24106                             
24107                         }
24108                     ]
24109                 }
24110             ]
24111         });
24112         
24113         time.createChild({
24114             tag: 'tr',
24115             cn: [
24116                 {
24117                     tag: 'td',
24118                     cn: [
24119                         {
24120                             tag: 'a',
24121                             href: '#',
24122                             cls: 'btn',
24123                             cn: [
24124                                 {
24125                                     tag: 'span',
24126                                     cls: 'hours-down fa fas fa-chevron-down'
24127                                 }
24128                             ]
24129                         }
24130                     ]
24131                 },
24132                 {
24133                     tag: 'td',
24134                     cls: 'separator'
24135                 },
24136                 {
24137                     tag: 'td',
24138                     cn: [
24139                         {
24140                             tag: 'a',
24141                             href: '#',
24142                             cls: 'btn',
24143                             cn: [
24144                                 {
24145                                     tag: 'span',
24146                                     cls: 'minutes-down fa fas fa-chevron-down'
24147                                 }
24148                             ]
24149                         }
24150                     ]
24151                 },
24152                 {
24153                     tag: 'td',
24154                     cls: 'separator'
24155                 }
24156             ]
24157         });
24158         
24159     },
24160     
24161     update: function()
24162     {
24163         
24164         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24165         
24166         this.fill();
24167     },
24168     
24169     fill: function() 
24170     {
24171         var hours = this.time.getHours();
24172         var minutes = this.time.getMinutes();
24173         var period = 'AM';
24174         
24175         if(hours > 11){
24176             period = 'PM';
24177         }
24178         
24179         if(hours == 0){
24180             hours = 12;
24181         }
24182         
24183         
24184         if(hours > 12){
24185             hours = hours - 12;
24186         }
24187         
24188         if(hours < 10){
24189             hours = '0' + hours;
24190         }
24191         
24192         if(minutes < 10){
24193             minutes = '0' + minutes;
24194         }
24195         
24196         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24197         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24198         this.pop.select('button', true).first().dom.innerHTML = period;
24199         
24200     },
24201     
24202     place: function()
24203     {   
24204         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24205         
24206         var cls = ['bottom'];
24207         
24208         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24209             cls.pop();
24210             cls.push('top');
24211         }
24212         
24213         cls.push('right');
24214         
24215         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24216             cls.pop();
24217             cls.push('left');
24218         }
24219         //this.picker().setXY(20000,20000);
24220         this.picker().addClass(cls.join('-'));
24221         
24222         var _this = this;
24223         
24224         Roo.each(cls, function(c){
24225             if(c == 'bottom'){
24226                 (function() {
24227                  //  
24228                 }).defer(200);
24229                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24230                 //_this.picker().setTop(_this.inputEl().getHeight());
24231                 return;
24232             }
24233             if(c == 'top'){
24234                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24235                 
24236                 //_this.picker().setTop(0 - _this.picker().getHeight());
24237                 return;
24238             }
24239             /*
24240             if(c == 'left'){
24241                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24242                 return;
24243             }
24244             if(c == 'right'){
24245                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24246                 return;
24247             }
24248             */
24249         });
24250         
24251     },
24252   
24253     onFocus : function()
24254     {
24255         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
24256         this.show();
24257     },
24258     
24259     onBlur : function()
24260     {
24261         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
24262         this.hide();
24263     },
24264     
24265     show : function()
24266     {
24267         this.picker().show();
24268         this.pop.show();
24269         this.update();
24270         this.place();
24271         
24272         this.fireEvent('show', this, this.date);
24273     },
24274     
24275     hide : function()
24276     {
24277         this.picker().hide();
24278         this.pop.hide();
24279         
24280         this.fireEvent('hide', this, this.date);
24281     },
24282     
24283     setTime : function()
24284     {
24285         this.hide();
24286         this.setValue(this.time.format(this.format));
24287         
24288         this.fireEvent('select', this, this.date);
24289         
24290         
24291     },
24292     
24293     onMousedown: function(e){
24294         e.stopPropagation();
24295         e.preventDefault();
24296     },
24297     
24298     onIncrementHours: function()
24299     {
24300         Roo.log('onIncrementHours');
24301         this.time = this.time.add(Date.HOUR, 1);
24302         this.update();
24303         
24304     },
24305     
24306     onDecrementHours: function()
24307     {
24308         Roo.log('onDecrementHours');
24309         this.time = this.time.add(Date.HOUR, -1);
24310         this.update();
24311     },
24312     
24313     onIncrementMinutes: function()
24314     {
24315         Roo.log('onIncrementMinutes');
24316         this.time = this.time.add(Date.MINUTE, 1);
24317         this.update();
24318     },
24319     
24320     onDecrementMinutes: function()
24321     {
24322         Roo.log('onDecrementMinutes');
24323         this.time = this.time.add(Date.MINUTE, -1);
24324         this.update();
24325     },
24326     
24327     onTogglePeriod: function()
24328     {
24329         Roo.log('onTogglePeriod');
24330         this.time = this.time.add(Date.HOUR, 12);
24331         this.update();
24332     }
24333     
24334    
24335 });
24336  
24337
24338 Roo.apply(Roo.bootstrap.TimeField,  {
24339   
24340     template : {
24341         tag: 'div',
24342         cls: 'datepicker dropdown-menu',
24343         cn: [
24344             {
24345                 tag: 'div',
24346                 cls: 'datepicker-time',
24347                 cn: [
24348                 {
24349                     tag: 'table',
24350                     cls: 'table-condensed',
24351                     cn:[
24352                         {
24353                             tag: 'tbody',
24354                             cn: [
24355                                 {
24356                                     tag: 'tr',
24357                                     cn: [
24358                                     {
24359                                         tag: 'td',
24360                                         colspan: '7'
24361                                     }
24362                                     ]
24363                                 }
24364                             ]
24365                         },
24366                         {
24367                             tag: 'tfoot',
24368                             cn: [
24369                                 {
24370                                     tag: 'tr',
24371                                     cn: [
24372                                     {
24373                                         tag: 'th',
24374                                         colspan: '7',
24375                                         cls: '',
24376                                         cn: [
24377                                             {
24378                                                 tag: 'button',
24379                                                 cls: 'btn btn-info ok',
24380                                                 html: 'OK'
24381                                             }
24382                                         ]
24383                                     }
24384                     
24385                                     ]
24386                                 }
24387                             ]
24388                         }
24389                     ]
24390                 }
24391                 ]
24392             }
24393         ]
24394     }
24395 });
24396
24397  
24398
24399  /*
24400  * - LGPL
24401  *
24402  * MonthField
24403  * 
24404  */
24405
24406 /**
24407  * @class Roo.bootstrap.MonthField
24408  * @extends Roo.bootstrap.Input
24409  * Bootstrap MonthField class
24410  * 
24411  * @cfg {String} language default en
24412  * 
24413  * @constructor
24414  * Create a new MonthField
24415  * @param {Object} config The config object
24416  */
24417
24418 Roo.bootstrap.MonthField = function(config){
24419     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
24420     
24421     this.addEvents({
24422         /**
24423          * @event show
24424          * Fires when this field show.
24425          * @param {Roo.bootstrap.MonthField} this
24426          * @param {Mixed} date The date value
24427          */
24428         show : true,
24429         /**
24430          * @event show
24431          * Fires when this field hide.
24432          * @param {Roo.bootstrap.MonthField} this
24433          * @param {Mixed} date The date value
24434          */
24435         hide : true,
24436         /**
24437          * @event select
24438          * Fires when select a date.
24439          * @param {Roo.bootstrap.MonthField} this
24440          * @param {String} oldvalue The old value
24441          * @param {String} newvalue The new value
24442          */
24443         select : true
24444     });
24445 };
24446
24447 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24448     
24449     onRender: function(ct, position)
24450     {
24451         
24452         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24453         
24454         this.language = this.language || 'en';
24455         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24456         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24457         
24458         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24459         this.isInline = false;
24460         this.isInput = true;
24461         this.component = this.el.select('.add-on', true).first() || false;
24462         this.component = (this.component && this.component.length === 0) ? false : this.component;
24463         this.hasInput = this.component && this.inputEL().length;
24464         
24465         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24466         
24467         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24468         
24469         this.picker().on('mousedown', this.onMousedown, this);
24470         this.picker().on('click', this.onClick, this);
24471         
24472         this.picker().addClass('datepicker-dropdown');
24473         
24474         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24475             v.setStyle('width', '189px');
24476         });
24477         
24478         this.fillMonths();
24479         
24480         this.update();
24481         
24482         if(this.isInline) {
24483             this.show();
24484         }
24485         
24486     },
24487     
24488     setValue: function(v, suppressEvent)
24489     {   
24490         var o = this.getValue();
24491         
24492         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24493         
24494         this.update();
24495
24496         if(suppressEvent !== true){
24497             this.fireEvent('select', this, o, v);
24498         }
24499         
24500     },
24501     
24502     getValue: function()
24503     {
24504         return this.value;
24505     },
24506     
24507     onClick: function(e) 
24508     {
24509         e.stopPropagation();
24510         e.preventDefault();
24511         
24512         var target = e.getTarget();
24513         
24514         if(target.nodeName.toLowerCase() === 'i'){
24515             target = Roo.get(target).dom.parentNode;
24516         }
24517         
24518         var nodeName = target.nodeName;
24519         var className = target.className;
24520         var html = target.innerHTML;
24521         
24522         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24523             return;
24524         }
24525         
24526         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24527         
24528         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24529         
24530         this.hide();
24531                         
24532     },
24533     
24534     picker : function()
24535     {
24536         return this.pickerEl;
24537     },
24538     
24539     fillMonths: function()
24540     {    
24541         var i = 0;
24542         var months = this.picker().select('>.datepicker-months td', true).first();
24543         
24544         months.dom.innerHTML = '';
24545         
24546         while (i < 12) {
24547             var month = {
24548                 tag: 'span',
24549                 cls: 'month',
24550                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24551             };
24552             
24553             months.createChild(month);
24554         }
24555         
24556     },
24557     
24558     update: function()
24559     {
24560         var _this = this;
24561         
24562         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24563             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24564         }
24565         
24566         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24567             e.removeClass('active');
24568             
24569             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24570                 e.addClass('active');
24571             }
24572         })
24573     },
24574     
24575     place: function()
24576     {
24577         if(this.isInline) {
24578             return;
24579         }
24580         
24581         this.picker().removeClass(['bottom', 'top']);
24582         
24583         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24584             /*
24585              * place to the top of element!
24586              *
24587              */
24588             
24589             this.picker().addClass('top');
24590             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24591             
24592             return;
24593         }
24594         
24595         this.picker().addClass('bottom');
24596         
24597         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24598     },
24599     
24600     onFocus : function()
24601     {
24602         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24603         this.show();
24604     },
24605     
24606     onBlur : function()
24607     {
24608         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24609         
24610         var d = this.inputEl().getValue();
24611         
24612         this.setValue(d);
24613                 
24614         this.hide();
24615     },
24616     
24617     show : function()
24618     {
24619         this.picker().show();
24620         this.picker().select('>.datepicker-months', true).first().show();
24621         this.update();
24622         this.place();
24623         
24624         this.fireEvent('show', this, this.date);
24625     },
24626     
24627     hide : function()
24628     {
24629         if(this.isInline) {
24630             return;
24631         }
24632         this.picker().hide();
24633         this.fireEvent('hide', this, this.date);
24634         
24635     },
24636     
24637     onMousedown: function(e)
24638     {
24639         e.stopPropagation();
24640         e.preventDefault();
24641     },
24642     
24643     keyup: function(e)
24644     {
24645         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24646         this.update();
24647     },
24648
24649     fireKey: function(e)
24650     {
24651         if (!this.picker().isVisible()){
24652             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24653                 this.show();
24654             }
24655             return;
24656         }
24657         
24658         var dir;
24659         
24660         switch(e.keyCode){
24661             case 27: // escape
24662                 this.hide();
24663                 e.preventDefault();
24664                 break;
24665             case 37: // left
24666             case 39: // right
24667                 dir = e.keyCode == 37 ? -1 : 1;
24668                 
24669                 this.vIndex = this.vIndex + dir;
24670                 
24671                 if(this.vIndex < 0){
24672                     this.vIndex = 0;
24673                 }
24674                 
24675                 if(this.vIndex > 11){
24676                     this.vIndex = 11;
24677                 }
24678                 
24679                 if(isNaN(this.vIndex)){
24680                     this.vIndex = 0;
24681                 }
24682                 
24683                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24684                 
24685                 break;
24686             case 38: // up
24687             case 40: // down
24688                 
24689                 dir = e.keyCode == 38 ? -1 : 1;
24690                 
24691                 this.vIndex = this.vIndex + dir * 4;
24692                 
24693                 if(this.vIndex < 0){
24694                     this.vIndex = 0;
24695                 }
24696                 
24697                 if(this.vIndex > 11){
24698                     this.vIndex = 11;
24699                 }
24700                 
24701                 if(isNaN(this.vIndex)){
24702                     this.vIndex = 0;
24703                 }
24704                 
24705                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24706                 break;
24707                 
24708             case 13: // enter
24709                 
24710                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24711                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24712                 }
24713                 
24714                 this.hide();
24715                 e.preventDefault();
24716                 break;
24717             case 9: // tab
24718                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24719                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24720                 }
24721                 this.hide();
24722                 break;
24723             case 16: // shift
24724             case 17: // ctrl
24725             case 18: // alt
24726                 break;
24727             default :
24728                 this.hide();
24729                 
24730         }
24731     },
24732     
24733     remove: function() 
24734     {
24735         this.picker().remove();
24736     }
24737    
24738 });
24739
24740 Roo.apply(Roo.bootstrap.MonthField,  {
24741     
24742     content : {
24743         tag: 'tbody',
24744         cn: [
24745         {
24746             tag: 'tr',
24747             cn: [
24748             {
24749                 tag: 'td',
24750                 colspan: '7'
24751             }
24752             ]
24753         }
24754         ]
24755     },
24756     
24757     dates:{
24758         en: {
24759             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24760             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24761         }
24762     }
24763 });
24764
24765 Roo.apply(Roo.bootstrap.MonthField,  {
24766   
24767     template : {
24768         tag: 'div',
24769         cls: 'datepicker dropdown-menu roo-dynamic',
24770         cn: [
24771             {
24772                 tag: 'div',
24773                 cls: 'datepicker-months',
24774                 cn: [
24775                 {
24776                     tag: 'table',
24777                     cls: 'table-condensed',
24778                     cn:[
24779                         Roo.bootstrap.DateField.content
24780                     ]
24781                 }
24782                 ]
24783             }
24784         ]
24785     }
24786 });
24787
24788  
24789
24790  
24791  /*
24792  * - LGPL
24793  *
24794  * CheckBox
24795  * 
24796  */
24797
24798 /**
24799  * @class Roo.bootstrap.CheckBox
24800  * @extends Roo.bootstrap.Input
24801  * Bootstrap CheckBox class
24802  * 
24803  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24804  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24805  * @cfg {String} boxLabel The text that appears beside the checkbox
24806  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24807  * @cfg {Boolean} checked initnal the element
24808  * @cfg {Boolean} inline inline the element (default false)
24809  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24810  * @cfg {String} tooltip label tooltip
24811  * 
24812  * @constructor
24813  * Create a new CheckBox
24814  * @param {Object} config The config object
24815  */
24816
24817 Roo.bootstrap.CheckBox = function(config){
24818     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24819    
24820     this.addEvents({
24821         /**
24822         * @event check
24823         * Fires when the element is checked or unchecked.
24824         * @param {Roo.bootstrap.CheckBox} this This input
24825         * @param {Boolean} checked The new checked value
24826         */
24827        check : true,
24828        /**
24829         * @event click
24830         * Fires when the element is click.
24831         * @param {Roo.bootstrap.CheckBox} this This input
24832         */
24833        click : true
24834     });
24835     
24836 };
24837
24838 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24839   
24840     inputType: 'checkbox',
24841     inputValue: 1,
24842     valueOff: 0,
24843     boxLabel: false,
24844     checked: false,
24845     weight : false,
24846     inline: false,
24847     tooltip : '',
24848     
24849     // checkbox success does not make any sense really.. 
24850     invalidClass : "",
24851     validClass : "",
24852     
24853     
24854     getAutoCreate : function()
24855     {
24856         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24857         
24858         var id = Roo.id();
24859         
24860         var cfg = {};
24861         
24862         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24863         
24864         if(this.inline){
24865             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24866         }
24867         
24868         var input =  {
24869             tag: 'input',
24870             id : id,
24871             type : this.inputType,
24872             value : this.inputValue,
24873             cls : 'roo-' + this.inputType, //'form-box',
24874             placeholder : this.placeholder || ''
24875             
24876         };
24877         
24878         if(this.inputType != 'radio'){
24879             var hidden =  {
24880                 tag: 'input',
24881                 type : 'hidden',
24882                 cls : 'roo-hidden-value',
24883                 value : this.checked ? this.inputValue : this.valueOff
24884             };
24885         }
24886         
24887             
24888         if (this.weight) { // Validity check?
24889             cfg.cls += " " + this.inputType + "-" + this.weight;
24890         }
24891         
24892         if (this.disabled) {
24893             input.disabled=true;
24894         }
24895         
24896         if(this.checked){
24897             input.checked = this.checked;
24898         }
24899         
24900         if (this.name) {
24901             
24902             input.name = this.name;
24903             
24904             if(this.inputType != 'radio'){
24905                 hidden.name = this.name;
24906                 input.name = '_hidden_' + this.name;
24907             }
24908         }
24909         
24910         if (this.size) {
24911             input.cls += ' input-' + this.size;
24912         }
24913         
24914         var settings=this;
24915         
24916         ['xs','sm','md','lg'].map(function(size){
24917             if (settings[size]) {
24918                 cfg.cls += ' col-' + size + '-' + settings[size];
24919             }
24920         });
24921         
24922         var inputblock = input;
24923          
24924         if (this.before || this.after) {
24925             
24926             inputblock = {
24927                 cls : 'input-group',
24928                 cn :  [] 
24929             };
24930             
24931             if (this.before) {
24932                 inputblock.cn.push({
24933                     tag :'span',
24934                     cls : 'input-group-addon',
24935                     html : this.before
24936                 });
24937             }
24938             
24939             inputblock.cn.push(input);
24940             
24941             if(this.inputType != 'radio'){
24942                 inputblock.cn.push(hidden);
24943             }
24944             
24945             if (this.after) {
24946                 inputblock.cn.push({
24947                     tag :'span',
24948                     cls : 'input-group-addon',
24949                     html : this.after
24950                 });
24951             }
24952             
24953         }
24954         var boxLabelCfg = false;
24955         
24956         if(this.boxLabel){
24957            
24958             boxLabelCfg = {
24959                 tag: 'label',
24960                 //'for': id, // box label is handled by onclick - so no for...
24961                 cls: 'box-label',
24962                 html: this.boxLabel
24963             };
24964             if(this.tooltip){
24965                 boxLabelCfg.tooltip = this.tooltip;
24966             }
24967              
24968         }
24969         
24970         
24971         if (align ==='left' && this.fieldLabel.length) {
24972 //                Roo.log("left and has label");
24973             cfg.cn = [
24974                 {
24975                     tag: 'label',
24976                     'for' :  id,
24977                     cls : 'control-label',
24978                     html : this.fieldLabel
24979                 },
24980                 {
24981                     cls : "", 
24982                     cn: [
24983                         inputblock
24984                     ]
24985                 }
24986             ];
24987             
24988             if (boxLabelCfg) {
24989                 cfg.cn[1].cn.push(boxLabelCfg);
24990             }
24991             
24992             if(this.labelWidth > 12){
24993                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24994             }
24995             
24996             if(this.labelWidth < 13 && this.labelmd == 0){
24997                 this.labelmd = this.labelWidth;
24998             }
24999             
25000             if(this.labellg > 0){
25001                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25002                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25003             }
25004             
25005             if(this.labelmd > 0){
25006                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25007                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25008             }
25009             
25010             if(this.labelsm > 0){
25011                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25012                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25013             }
25014             
25015             if(this.labelxs > 0){
25016                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25017                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25018             }
25019             
25020         } else if ( this.fieldLabel.length) {
25021 //                Roo.log(" label");
25022                 cfg.cn = [
25023                    
25024                     {
25025                         tag: this.boxLabel ? 'span' : 'label',
25026                         'for': id,
25027                         cls: 'control-label box-input-label',
25028                         //cls : 'input-group-addon',
25029                         html : this.fieldLabel
25030                     },
25031                     
25032                     inputblock
25033                     
25034                 ];
25035                 if (boxLabelCfg) {
25036                     cfg.cn.push(boxLabelCfg);
25037                 }
25038
25039         } else {
25040             
25041 //                Roo.log(" no label && no align");
25042                 cfg.cn = [  inputblock ] ;
25043                 if (boxLabelCfg) {
25044                     cfg.cn.push(boxLabelCfg);
25045                 }
25046
25047                 
25048         }
25049         
25050        
25051         
25052         if(this.inputType != 'radio'){
25053             cfg.cn.push(hidden);
25054         }
25055         
25056         return cfg;
25057         
25058     },
25059     
25060     /**
25061      * return the real input element.
25062      */
25063     inputEl: function ()
25064     {
25065         return this.el.select('input.roo-' + this.inputType,true).first();
25066     },
25067     hiddenEl: function ()
25068     {
25069         return this.el.select('input.roo-hidden-value',true).first();
25070     },
25071     
25072     labelEl: function()
25073     {
25074         return this.el.select('label.control-label',true).first();
25075     },
25076     /* depricated... */
25077     
25078     label: function()
25079     {
25080         return this.labelEl();
25081     },
25082     
25083     boxLabelEl: function()
25084     {
25085         return this.el.select('label.box-label',true).first();
25086     },
25087     
25088     initEvents : function()
25089     {
25090 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
25091         
25092         this.inputEl().on('click', this.onClick,  this);
25093         
25094         if (this.boxLabel) { 
25095             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25096         }
25097         
25098         this.startValue = this.getValue();
25099         
25100         if(this.groupId){
25101             Roo.bootstrap.CheckBox.register(this);
25102         }
25103     },
25104     
25105     onClick : function(e)
25106     {   
25107         if(this.fireEvent('click', this, e) !== false){
25108             this.setChecked(!this.checked);
25109         }
25110         
25111     },
25112     
25113     setChecked : function(state,suppressEvent)
25114     {
25115         this.startValue = this.getValue();
25116
25117         if(this.inputType == 'radio'){
25118             
25119             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25120                 e.dom.checked = false;
25121             });
25122             
25123             this.inputEl().dom.checked = true;
25124             
25125             this.inputEl().dom.value = this.inputValue;
25126             
25127             if(suppressEvent !== true){
25128                 this.fireEvent('check', this, true);
25129             }
25130             
25131             this.validate();
25132             
25133             return;
25134         }
25135         
25136         this.checked = state;
25137         
25138         this.inputEl().dom.checked = state;
25139         
25140         
25141         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25142         
25143         if(suppressEvent !== true){
25144             this.fireEvent('check', this, state);
25145         }
25146         
25147         this.validate();
25148     },
25149     
25150     getValue : function()
25151     {
25152         if(this.inputType == 'radio'){
25153             return this.getGroupValue();
25154         }
25155         
25156         return this.hiddenEl().dom.value;
25157         
25158     },
25159     
25160     getGroupValue : function()
25161     {
25162         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25163             return '';
25164         }
25165         
25166         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25167     },
25168     
25169     setValue : function(v,suppressEvent)
25170     {
25171         if(this.inputType == 'radio'){
25172             this.setGroupValue(v, suppressEvent);
25173             return;
25174         }
25175         
25176         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25177         
25178         this.validate();
25179     },
25180     
25181     setGroupValue : function(v, suppressEvent)
25182     {
25183         this.startValue = this.getValue();
25184         
25185         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25186             e.dom.checked = false;
25187             
25188             if(e.dom.value == v){
25189                 e.dom.checked = true;
25190             }
25191         });
25192         
25193         if(suppressEvent !== true){
25194             this.fireEvent('check', this, true);
25195         }
25196
25197         this.validate();
25198         
25199         return;
25200     },
25201     
25202     validate : function()
25203     {
25204         if(this.getVisibilityEl().hasClass('hidden')){
25205             return true;
25206         }
25207         
25208         if(
25209                 this.disabled || 
25210                 (this.inputType == 'radio' && this.validateRadio()) ||
25211                 (this.inputType == 'checkbox' && this.validateCheckbox())
25212         ){
25213             this.markValid();
25214             return true;
25215         }
25216         
25217         this.markInvalid();
25218         return false;
25219     },
25220     
25221     validateRadio : function()
25222     {
25223         if(this.getVisibilityEl().hasClass('hidden')){
25224             return true;
25225         }
25226         
25227         if(this.allowBlank){
25228             return true;
25229         }
25230         
25231         var valid = false;
25232         
25233         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25234             if(!e.dom.checked){
25235                 return;
25236             }
25237             
25238             valid = true;
25239             
25240             return false;
25241         });
25242         
25243         return valid;
25244     },
25245     
25246     validateCheckbox : function()
25247     {
25248         if(!this.groupId){
25249             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25250             //return (this.getValue() == this.inputValue) ? true : false;
25251         }
25252         
25253         var group = Roo.bootstrap.CheckBox.get(this.groupId);
25254         
25255         if(!group){
25256             return false;
25257         }
25258         
25259         var r = false;
25260         
25261         for(var i in group){
25262             if(group[i].el.isVisible(true)){
25263                 r = false;
25264                 break;
25265             }
25266             
25267             r = true;
25268         }
25269         
25270         for(var i in group){
25271             if(r){
25272                 break;
25273             }
25274             
25275             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25276         }
25277         
25278         return r;
25279     },
25280     
25281     /**
25282      * Mark this field as valid
25283      */
25284     markValid : function()
25285     {
25286         var _this = this;
25287         
25288         this.fireEvent('valid', this);
25289         
25290         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25291         
25292         if(this.groupId){
25293             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
25294         }
25295         
25296         if(label){
25297             label.markValid();
25298         }
25299
25300         if(this.inputType == 'radio'){
25301             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25302                 var fg = e.findParent('.form-group', false, true);
25303                 if (Roo.bootstrap.version == 3) {
25304                     fg.removeClass([_this.invalidClass, _this.validClass]);
25305                     fg.addClass(_this.validClass);
25306                 } else {
25307                     fg.removeClass(['is-valid', 'is-invalid']);
25308                     fg.addClass('is-valid');
25309                 }
25310             });
25311             
25312             return;
25313         }
25314
25315         if(!this.groupId){
25316             var fg = this.el.findParent('.form-group', false, true);
25317             if (Roo.bootstrap.version == 3) {
25318                 fg.removeClass([this.invalidClass, this.validClass]);
25319                 fg.addClass(this.validClass);
25320             } else {
25321                 fg.removeClass(['is-valid', 'is-invalid']);
25322                 fg.addClass('is-valid');
25323             }
25324             return;
25325         }
25326         
25327         var group = Roo.bootstrap.CheckBox.get(this.groupId);
25328         
25329         if(!group){
25330             return;
25331         }
25332         
25333         for(var i in group){
25334             var fg = group[i].el.findParent('.form-group', false, true);
25335             if (Roo.bootstrap.version == 3) {
25336                 fg.removeClass([this.invalidClass, this.validClass]);
25337                 fg.addClass(this.validClass);
25338             } else {
25339                 fg.removeClass(['is-valid', 'is-invalid']);
25340                 fg.addClass('is-valid');
25341             }
25342         }
25343     },
25344     
25345      /**
25346      * Mark this field as invalid
25347      * @param {String} msg The validation message
25348      */
25349     markInvalid : function(msg)
25350     {
25351         if(this.allowBlank){
25352             return;
25353         }
25354         
25355         var _this = this;
25356         
25357         this.fireEvent('invalid', this, msg);
25358         
25359         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25360         
25361         if(this.groupId){
25362             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
25363         }
25364         
25365         if(label){
25366             label.markInvalid();
25367         }
25368             
25369         if(this.inputType == 'radio'){
25370             
25371             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25372                 var fg = e.findParent('.form-group', false, true);
25373                 if (Roo.bootstrap.version == 3) {
25374                     fg.removeClass([_this.invalidClass, _this.validClass]);
25375                     fg.addClass(_this.invalidClass);
25376                 } else {
25377                     fg.removeClass(['is-invalid', 'is-valid']);
25378                     fg.addClass('is-invalid');
25379                 }
25380             });
25381             
25382             return;
25383         }
25384         
25385         if(!this.groupId){
25386             var fg = this.el.findParent('.form-group', false, true);
25387             if (Roo.bootstrap.version == 3) {
25388                 fg.removeClass([_this.invalidClass, _this.validClass]);
25389                 fg.addClass(_this.invalidClass);
25390             } else {
25391                 fg.removeClass(['is-invalid', 'is-valid']);
25392                 fg.addClass('is-invalid');
25393             }
25394             return;
25395         }
25396         
25397         var group = Roo.bootstrap.CheckBox.get(this.groupId);
25398         
25399         if(!group){
25400             return;
25401         }
25402         
25403         for(var i in group){
25404             var fg = group[i].el.findParent('.form-group', false, true);
25405             if (Roo.bootstrap.version == 3) {
25406                 fg.removeClass([_this.invalidClass, _this.validClass]);
25407                 fg.addClass(_this.invalidClass);
25408             } else {
25409                 fg.removeClass(['is-invalid', 'is-valid']);
25410                 fg.addClass('is-invalid');
25411             }
25412         }
25413         
25414     },
25415     
25416     clearInvalid : function()
25417     {
25418         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
25419         
25420         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25421         
25422         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25423         
25424         if (label && label.iconEl) {
25425             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25426             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25427         }
25428     },
25429     
25430     disable : function()
25431     {
25432         if(this.inputType != 'radio'){
25433             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25434             return;
25435         }
25436         
25437         var _this = this;
25438         
25439         if(this.rendered){
25440             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25441                 _this.getActionEl().addClass(this.disabledClass);
25442                 e.dom.disabled = true;
25443             });
25444         }
25445         
25446         this.disabled = true;
25447         this.fireEvent("disable", this);
25448         return this;
25449     },
25450
25451     enable : function()
25452     {
25453         if(this.inputType != 'radio'){
25454             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25455             return;
25456         }
25457         
25458         var _this = this;
25459         
25460         if(this.rendered){
25461             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25462                 _this.getActionEl().removeClass(this.disabledClass);
25463                 e.dom.disabled = false;
25464             });
25465         }
25466         
25467         this.disabled = false;
25468         this.fireEvent("enable", this);
25469         return this;
25470     },
25471     
25472     setBoxLabel : function(v)
25473     {
25474         this.boxLabel = v;
25475         
25476         if(this.rendered){
25477             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25478         }
25479     }
25480
25481 });
25482
25483 Roo.apply(Roo.bootstrap.CheckBox, {
25484     
25485     groups: {},
25486     
25487      /**
25488     * register a CheckBox Group
25489     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25490     */
25491     register : function(checkbox)
25492     {
25493         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25494             this.groups[checkbox.groupId] = {};
25495         }
25496         
25497         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25498             return;
25499         }
25500         
25501         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25502         
25503     },
25504     /**
25505     * fetch a CheckBox Group based on the group ID
25506     * @param {string} the group ID
25507     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25508     */
25509     get: function(groupId) {
25510         if (typeof(this.groups[groupId]) == 'undefined') {
25511             return false;
25512         }
25513         
25514         return this.groups[groupId] ;
25515     }
25516     
25517     
25518 });
25519 /*
25520  * - LGPL
25521  *
25522  * RadioItem
25523  * 
25524  */
25525
25526 /**
25527  * @class Roo.bootstrap.Radio
25528  * @extends Roo.bootstrap.Component
25529  * Bootstrap Radio class
25530  * @cfg {String} boxLabel - the label associated
25531  * @cfg {String} value - the value of radio
25532  * 
25533  * @constructor
25534  * Create a new Radio
25535  * @param {Object} config The config object
25536  */
25537 Roo.bootstrap.Radio = function(config){
25538     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25539     
25540 };
25541
25542 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25543     
25544     boxLabel : '',
25545     
25546     value : '',
25547     
25548     getAutoCreate : function()
25549     {
25550         var cfg = {
25551             tag : 'div',
25552             cls : 'form-group radio',
25553             cn : [
25554                 {
25555                     tag : 'label',
25556                     cls : 'box-label',
25557                     html : this.boxLabel
25558                 }
25559             ]
25560         };
25561         
25562         return cfg;
25563     },
25564     
25565     initEvents : function() 
25566     {
25567         this.parent().register(this);
25568         
25569         this.el.on('click', this.onClick, this);
25570         
25571     },
25572     
25573     onClick : function(e)
25574     {
25575         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25576             this.setChecked(true);
25577         }
25578     },
25579     
25580     setChecked : function(state, suppressEvent)
25581     {
25582         this.parent().setValue(this.value, suppressEvent);
25583         
25584     },
25585     
25586     setBoxLabel : function(v)
25587     {
25588         this.boxLabel = v;
25589         
25590         if(this.rendered){
25591             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25592         }
25593     }
25594     
25595 });
25596  
25597
25598  /*
25599  * - LGPL
25600  *
25601  * Input
25602  * 
25603  */
25604
25605 /**
25606  * @class Roo.bootstrap.SecurePass
25607  * @extends Roo.bootstrap.Input
25608  * Bootstrap SecurePass class
25609  *
25610  * 
25611  * @constructor
25612  * Create a new SecurePass
25613  * @param {Object} config The config object
25614  */
25615  
25616 Roo.bootstrap.SecurePass = function (config) {
25617     // these go here, so the translation tool can replace them..
25618     this.errors = {
25619         PwdEmpty: "Please type a password, and then retype it to confirm.",
25620         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25621         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25622         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25623         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25624         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25625         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25626         TooWeak: "Your password is Too Weak."
25627     },
25628     this.meterLabel = "Password strength:";
25629     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25630     this.meterClass = [
25631         "roo-password-meter-tooweak", 
25632         "roo-password-meter-weak", 
25633         "roo-password-meter-medium", 
25634         "roo-password-meter-strong", 
25635         "roo-password-meter-grey"
25636     ];
25637     
25638     this.errors = {};
25639     
25640     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25641 }
25642
25643 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25644     /**
25645      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25646      * {
25647      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25648      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25649      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25650      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25651      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25652      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25653      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25654      * })
25655      */
25656     // private
25657     
25658     meterWidth: 300,
25659     errorMsg :'',    
25660     errors: false,
25661     imageRoot: '/',
25662     /**
25663      * @cfg {String/Object} Label for the strength meter (defaults to
25664      * 'Password strength:')
25665      */
25666     // private
25667     meterLabel: '',
25668     /**
25669      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25670      * ['Weak', 'Medium', 'Strong'])
25671      */
25672     // private    
25673     pwdStrengths: false,    
25674     // private
25675     strength: 0,
25676     // private
25677     _lastPwd: null,
25678     // private
25679     kCapitalLetter: 0,
25680     kSmallLetter: 1,
25681     kDigit: 2,
25682     kPunctuation: 3,
25683     
25684     insecure: false,
25685     // private
25686     initEvents: function ()
25687     {
25688         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25689
25690         if (this.el.is('input[type=password]') && Roo.isSafari) {
25691             this.el.on('keydown', this.SafariOnKeyDown, this);
25692         }
25693
25694         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25695     },
25696     // private
25697     onRender: function (ct, position)
25698     {
25699         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25700         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25701         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25702
25703         this.trigger.createChild({
25704                    cn: [
25705                     {
25706                     //id: 'PwdMeter',
25707                     tag: 'div',
25708                     cls: 'roo-password-meter-grey col-xs-12',
25709                     style: {
25710                         //width: 0,
25711                         //width: this.meterWidth + 'px'                                                
25712                         }
25713                     },
25714                     {                            
25715                          cls: 'roo-password-meter-text'                          
25716                     }
25717                 ]            
25718         });
25719
25720          
25721         if (this.hideTrigger) {
25722             this.trigger.setDisplayed(false);
25723         }
25724         this.setSize(this.width || '', this.height || '');
25725     },
25726     // private
25727     onDestroy: function ()
25728     {
25729         if (this.trigger) {
25730             this.trigger.removeAllListeners();
25731             this.trigger.remove();
25732         }
25733         if (this.wrap) {
25734             this.wrap.remove();
25735         }
25736         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25737     },
25738     // private
25739     checkStrength: function ()
25740     {
25741         var pwd = this.inputEl().getValue();
25742         if (pwd == this._lastPwd) {
25743             return;
25744         }
25745
25746         var strength;
25747         if (this.ClientSideStrongPassword(pwd)) {
25748             strength = 3;
25749         } else if (this.ClientSideMediumPassword(pwd)) {
25750             strength = 2;
25751         } else if (this.ClientSideWeakPassword(pwd)) {
25752             strength = 1;
25753         } else {
25754             strength = 0;
25755         }
25756         
25757         Roo.log('strength1: ' + strength);
25758         
25759         //var pm = this.trigger.child('div/div/div').dom;
25760         var pm = this.trigger.child('div/div');
25761         pm.removeClass(this.meterClass);
25762         pm.addClass(this.meterClass[strength]);
25763                 
25764         
25765         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25766                 
25767         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25768         
25769         this._lastPwd = pwd;
25770     },
25771     reset: function ()
25772     {
25773         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25774         
25775         this._lastPwd = '';
25776         
25777         var pm = this.trigger.child('div/div');
25778         pm.removeClass(this.meterClass);
25779         pm.addClass('roo-password-meter-grey');        
25780         
25781         
25782         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25783         
25784         pt.innerHTML = '';
25785         this.inputEl().dom.type='password';
25786     },
25787     // private
25788     validateValue: function (value)
25789     {
25790         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25791             return false;
25792         }
25793         if (value.length == 0) {
25794             if (this.allowBlank) {
25795                 this.clearInvalid();
25796                 return true;
25797             }
25798
25799             this.markInvalid(this.errors.PwdEmpty);
25800             this.errorMsg = this.errors.PwdEmpty;
25801             return false;
25802         }
25803         
25804         if(this.insecure){
25805             return true;
25806         }
25807         
25808         if (!value.match(/[\x21-\x7e]+/)) {
25809             this.markInvalid(this.errors.PwdBadChar);
25810             this.errorMsg = this.errors.PwdBadChar;
25811             return false;
25812         }
25813         if (value.length < 6) {
25814             this.markInvalid(this.errors.PwdShort);
25815             this.errorMsg = this.errors.PwdShort;
25816             return false;
25817         }
25818         if (value.length > 16) {
25819             this.markInvalid(this.errors.PwdLong);
25820             this.errorMsg = this.errors.PwdLong;
25821             return false;
25822         }
25823         var strength;
25824         if (this.ClientSideStrongPassword(value)) {
25825             strength = 3;
25826         } else if (this.ClientSideMediumPassword(value)) {
25827             strength = 2;
25828         } else if (this.ClientSideWeakPassword(value)) {
25829             strength = 1;
25830         } else {
25831             strength = 0;
25832         }
25833
25834         
25835         if (strength < 2) {
25836             //this.markInvalid(this.errors.TooWeak);
25837             this.errorMsg = this.errors.TooWeak;
25838             //return false;
25839         }
25840         
25841         
25842         console.log('strength2: ' + strength);
25843         
25844         //var pm = this.trigger.child('div/div/div').dom;
25845         
25846         var pm = this.trigger.child('div/div');
25847         pm.removeClass(this.meterClass);
25848         pm.addClass(this.meterClass[strength]);
25849                 
25850         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25851                 
25852         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25853         
25854         this.errorMsg = ''; 
25855         return true;
25856     },
25857     // private
25858     CharacterSetChecks: function (type)
25859     {
25860         this.type = type;
25861         this.fResult = false;
25862     },
25863     // private
25864     isctype: function (character, type)
25865     {
25866         switch (type) {  
25867             case this.kCapitalLetter:
25868                 if (character >= 'A' && character <= 'Z') {
25869                     return true;
25870                 }
25871                 break;
25872             
25873             case this.kSmallLetter:
25874                 if (character >= 'a' && character <= 'z') {
25875                     return true;
25876                 }
25877                 break;
25878             
25879             case this.kDigit:
25880                 if (character >= '0' && character <= '9') {
25881                     return true;
25882                 }
25883                 break;
25884             
25885             case this.kPunctuation:
25886                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25887                     return true;
25888                 }
25889                 break;
25890             
25891             default:
25892                 return false;
25893         }
25894
25895     },
25896     // private
25897     IsLongEnough: function (pwd, size)
25898     {
25899         return !(pwd == null || isNaN(size) || pwd.length < size);
25900     },
25901     // private
25902     SpansEnoughCharacterSets: function (word, nb)
25903     {
25904         if (!this.IsLongEnough(word, nb))
25905         {
25906             return false;
25907         }
25908
25909         var characterSetChecks = new Array(
25910             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25911             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25912         );
25913         
25914         for (var index = 0; index < word.length; ++index) {
25915             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25916                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25917                     characterSetChecks[nCharSet].fResult = true;
25918                     break;
25919                 }
25920             }
25921         }
25922
25923         var nCharSets = 0;
25924         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25925             if (characterSetChecks[nCharSet].fResult) {
25926                 ++nCharSets;
25927             }
25928         }
25929
25930         if (nCharSets < nb) {
25931             return false;
25932         }
25933         return true;
25934     },
25935     // private
25936     ClientSideStrongPassword: function (pwd)
25937     {
25938         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25939     },
25940     // private
25941     ClientSideMediumPassword: function (pwd)
25942     {
25943         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25944     },
25945     // private
25946     ClientSideWeakPassword: function (pwd)
25947     {
25948         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25949     }
25950           
25951 })//<script type="text/javascript">
25952
25953 /*
25954  * Based  Ext JS Library 1.1.1
25955  * Copyright(c) 2006-2007, Ext JS, LLC.
25956  * LGPL
25957  *
25958  */
25959  
25960 /**
25961  * @class Roo.HtmlEditorCore
25962  * @extends Roo.Component
25963  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25964  *
25965  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25966  */
25967
25968 Roo.HtmlEditorCore = function(config){
25969     
25970     
25971     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25972     
25973     
25974     this.addEvents({
25975         /**
25976          * @event initialize
25977          * Fires when the editor is fully initialized (including the iframe)
25978          * @param {Roo.HtmlEditorCore} this
25979          */
25980         initialize: true,
25981         /**
25982          * @event activate
25983          * Fires when the editor is first receives the focus. Any insertion must wait
25984          * until after this event.
25985          * @param {Roo.HtmlEditorCore} this
25986          */
25987         activate: true,
25988          /**
25989          * @event beforesync
25990          * Fires before the textarea is updated with content from the editor iframe. Return false
25991          * to cancel the sync.
25992          * @param {Roo.HtmlEditorCore} this
25993          * @param {String} html
25994          */
25995         beforesync: true,
25996          /**
25997          * @event beforepush
25998          * Fires before the iframe editor is updated with content from the textarea. Return false
25999          * to cancel the push.
26000          * @param {Roo.HtmlEditorCore} this
26001          * @param {String} html
26002          */
26003         beforepush: true,
26004          /**
26005          * @event sync
26006          * Fires when the textarea is updated with content from the editor iframe.
26007          * @param {Roo.HtmlEditorCore} this
26008          * @param {String} html
26009          */
26010         sync: true,
26011          /**
26012          * @event push
26013          * Fires when the iframe editor is updated with content from the textarea.
26014          * @param {Roo.HtmlEditorCore} this
26015          * @param {String} html
26016          */
26017         push: true,
26018         
26019         /**
26020          * @event editorevent
26021          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26022          * @param {Roo.HtmlEditorCore} this
26023          */
26024         editorevent: true
26025         
26026     });
26027     
26028     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26029     
26030     // defaults : white / black...
26031     this.applyBlacklists();
26032     
26033     
26034     
26035 };
26036
26037
26038 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26039
26040
26041      /**
26042      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26043      */
26044     
26045     owner : false,
26046     
26047      /**
26048      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26049      *                        Roo.resizable.
26050      */
26051     resizable : false,
26052      /**
26053      * @cfg {Number} height (in pixels)
26054      */   
26055     height: 300,
26056    /**
26057      * @cfg {Number} width (in pixels)
26058      */   
26059     width: 500,
26060     
26061     /**
26062      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26063      * 
26064      */
26065     stylesheets: false,
26066     
26067     /**
26068      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26069      */
26070     allowComments: false,
26071     // id of frame..
26072     frameId: false,
26073     
26074     // private properties
26075     validationEvent : false,
26076     deferHeight: true,
26077     initialized : false,
26078     activated : false,
26079     sourceEditMode : false,
26080     onFocus : Roo.emptyFn,
26081     iframePad:3,
26082     hideMode:'offsets',
26083     
26084     clearUp: true,
26085     
26086     // blacklist + whitelisted elements..
26087     black: false,
26088     white: false,
26089      
26090     bodyCls : '',
26091
26092     /**
26093      * Protected method that will not generally be called directly. It
26094      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26095      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26096      */
26097     getDocMarkup : function(){
26098         // body styles..
26099         var st = '';
26100         
26101         // inherit styels from page...?? 
26102         if (this.stylesheets === false) {
26103             
26104             Roo.get(document.head).select('style').each(function(node) {
26105                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26106             });
26107             
26108             Roo.get(document.head).select('link').each(function(node) { 
26109                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26110             });
26111             
26112         } else if (!this.stylesheets.length) {
26113                 // simple..
26114                 st = '<style type="text/css">' +
26115                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26116                    '</style>';
26117         } else {
26118             for (var i in this.stylesheets) { 
26119                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26120             }
26121             
26122         }
26123         
26124         st +=  '<style type="text/css">' +
26125             'IMG { cursor: pointer } ' +
26126         '</style>';
26127
26128         var cls = 'roo-htmleditor-body';
26129         
26130         if(this.bodyCls.length){
26131             cls += ' ' + this.bodyCls;
26132         }
26133         
26134         return '<html><head>' + st  +
26135             //<style type="text/css">' +
26136             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26137             //'</style>' +
26138             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26139     },
26140
26141     // private
26142     onRender : function(ct, position)
26143     {
26144         var _t = this;
26145         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26146         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26147         
26148         
26149         this.el.dom.style.border = '0 none';
26150         this.el.dom.setAttribute('tabIndex', -1);
26151         this.el.addClass('x-hidden hide');
26152         
26153         
26154         
26155         if(Roo.isIE){ // fix IE 1px bogus margin
26156             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26157         }
26158        
26159         
26160         this.frameId = Roo.id();
26161         
26162          
26163         
26164         var iframe = this.owner.wrap.createChild({
26165             tag: 'iframe',
26166             cls: 'form-control', // bootstrap..
26167             id: this.frameId,
26168             name: this.frameId,
26169             frameBorder : 'no',
26170             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26171         }, this.el
26172         );
26173         
26174         
26175         this.iframe = iframe.dom;
26176
26177          this.assignDocWin();
26178         
26179         this.doc.designMode = 'on';
26180        
26181         this.doc.open();
26182         this.doc.write(this.getDocMarkup());
26183         this.doc.close();
26184
26185         
26186         var task = { // must defer to wait for browser to be ready
26187             run : function(){
26188                 //console.log("run task?" + this.doc.readyState);
26189                 this.assignDocWin();
26190                 if(this.doc.body || this.doc.readyState == 'complete'){
26191                     try {
26192                         this.doc.designMode="on";
26193                     } catch (e) {
26194                         return;
26195                     }
26196                     Roo.TaskMgr.stop(task);
26197                     this.initEditor.defer(10, this);
26198                 }
26199             },
26200             interval : 10,
26201             duration: 10000,
26202             scope: this
26203         };
26204         Roo.TaskMgr.start(task);
26205
26206     },
26207
26208     // private
26209     onResize : function(w, h)
26210     {
26211          Roo.log('resize: ' +w + ',' + h );
26212         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26213         if(!this.iframe){
26214             return;
26215         }
26216         if(typeof w == 'number'){
26217             
26218             this.iframe.style.width = w + 'px';
26219         }
26220         if(typeof h == 'number'){
26221             
26222             this.iframe.style.height = h + 'px';
26223             if(this.doc){
26224                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26225             }
26226         }
26227         
26228     },
26229
26230     /**
26231      * Toggles the editor between standard and source edit mode.
26232      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26233      */
26234     toggleSourceEdit : function(sourceEditMode){
26235         
26236         this.sourceEditMode = sourceEditMode === true;
26237         
26238         if(this.sourceEditMode){
26239  
26240             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26241             
26242         }else{
26243             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26244             //this.iframe.className = '';
26245             this.deferFocus();
26246         }
26247         //this.setSize(this.owner.wrap.getSize());
26248         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26249     },
26250
26251     
26252   
26253
26254     /**
26255      * Protected method that will not generally be called directly. If you need/want
26256      * custom HTML cleanup, this is the method you should override.
26257      * @param {String} html The HTML to be cleaned
26258      * return {String} The cleaned HTML
26259      */
26260     cleanHtml : function(html){
26261         html = String(html);
26262         if(html.length > 5){
26263             if(Roo.isSafari){ // strip safari nonsense
26264                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26265             }
26266         }
26267         if(html == '&nbsp;'){
26268             html = '';
26269         }
26270         return html;
26271     },
26272
26273     /**
26274      * HTML Editor -> Textarea
26275      * Protected method that will not generally be called directly. Syncs the contents
26276      * of the editor iframe with the textarea.
26277      */
26278     syncValue : function(){
26279         if(this.initialized){
26280             var bd = (this.doc.body || this.doc.documentElement);
26281             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26282             var html = bd.innerHTML;
26283             if(Roo.isSafari){
26284                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26285                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26286                 if(m && m[1]){
26287                     html = '<div style="'+m[0]+'">' + html + '</div>';
26288                 }
26289             }
26290             html = this.cleanHtml(html);
26291             // fix up the special chars.. normaly like back quotes in word...
26292             // however we do not want to do this with chinese..
26293             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26294                 
26295                 var cc = match.charCodeAt();
26296
26297                 // Get the character value, handling surrogate pairs
26298                 if (match.length == 2) {
26299                     // It's a surrogate pair, calculate the Unicode code point
26300                     var high = match.charCodeAt(0) - 0xD800;
26301                     var low  = match.charCodeAt(1) - 0xDC00;
26302                     cc = (high * 0x400) + low + 0x10000;
26303                 }  else if (
26304                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26305                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26306                     (cc >= 0xf900 && cc < 0xfb00 )
26307                 ) {
26308                         return match;
26309                 }  
26310          
26311                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26312                 return "&#" + cc + ";";
26313                 
26314                 
26315             });
26316             
26317             
26318              
26319             if(this.owner.fireEvent('beforesync', this, html) !== false){
26320                 this.el.dom.value = html;
26321                 this.owner.fireEvent('sync', this, html);
26322             }
26323         }
26324     },
26325
26326     /**
26327      * Protected method that will not generally be called directly. Pushes the value of the textarea
26328      * into the iframe editor.
26329      */
26330     pushValue : function(){
26331         if(this.initialized){
26332             var v = this.el.dom.value.trim();
26333             
26334 //            if(v.length < 1){
26335 //                v = '&#160;';
26336 //            }
26337             
26338             if(this.owner.fireEvent('beforepush', this, v) !== false){
26339                 var d = (this.doc.body || this.doc.documentElement);
26340                 d.innerHTML = v;
26341                 this.cleanUpPaste();
26342                 this.el.dom.value = d.innerHTML;
26343                 this.owner.fireEvent('push', this, v);
26344             }
26345         }
26346     },
26347
26348     // private
26349     deferFocus : function(){
26350         this.focus.defer(10, this);
26351     },
26352
26353     // doc'ed in Field
26354     focus : function(){
26355         if(this.win && !this.sourceEditMode){
26356             this.win.focus();
26357         }else{
26358             this.el.focus();
26359         }
26360     },
26361     
26362     assignDocWin: function()
26363     {
26364         var iframe = this.iframe;
26365         
26366          if(Roo.isIE){
26367             this.doc = iframe.contentWindow.document;
26368             this.win = iframe.contentWindow;
26369         } else {
26370 //            if (!Roo.get(this.frameId)) {
26371 //                return;
26372 //            }
26373 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26374 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26375             
26376             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26377                 return;
26378             }
26379             
26380             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26381             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26382         }
26383     },
26384     
26385     // private
26386     initEditor : function(){
26387         //console.log("INIT EDITOR");
26388         this.assignDocWin();
26389         
26390         
26391         
26392         this.doc.designMode="on";
26393         this.doc.open();
26394         this.doc.write(this.getDocMarkup());
26395         this.doc.close();
26396         
26397         var dbody = (this.doc.body || this.doc.documentElement);
26398         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26399         // this copies styles from the containing element into thsi one..
26400         // not sure why we need all of this..
26401         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26402         
26403         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26404         //ss['background-attachment'] = 'fixed'; // w3c
26405         dbody.bgProperties = 'fixed'; // ie
26406         //Roo.DomHelper.applyStyles(dbody, ss);
26407         Roo.EventManager.on(this.doc, {
26408             //'mousedown': this.onEditorEvent,
26409             'mouseup': this.onEditorEvent,
26410             'dblclick': this.onEditorEvent,
26411             'click': this.onEditorEvent,
26412             'keyup': this.onEditorEvent,
26413             buffer:100,
26414             scope: this
26415         });
26416         if(Roo.isGecko){
26417             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26418         }
26419         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26420             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26421         }
26422         this.initialized = true;
26423
26424         this.owner.fireEvent('initialize', this);
26425         this.pushValue();
26426     },
26427
26428     // private
26429     onDestroy : function(){
26430         
26431         
26432         
26433         if(this.rendered){
26434             
26435             //for (var i =0; i < this.toolbars.length;i++) {
26436             //    // fixme - ask toolbars for heights?
26437             //    this.toolbars[i].onDestroy();
26438            // }
26439             
26440             //this.wrap.dom.innerHTML = '';
26441             //this.wrap.remove();
26442         }
26443     },
26444
26445     // private
26446     onFirstFocus : function(){
26447         
26448         this.assignDocWin();
26449         
26450         
26451         this.activated = true;
26452          
26453     
26454         if(Roo.isGecko){ // prevent silly gecko errors
26455             this.win.focus();
26456             var s = this.win.getSelection();
26457             if(!s.focusNode || s.focusNode.nodeType != 3){
26458                 var r = s.getRangeAt(0);
26459                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26460                 r.collapse(true);
26461                 this.deferFocus();
26462             }
26463             try{
26464                 this.execCmd('useCSS', true);
26465                 this.execCmd('styleWithCSS', false);
26466             }catch(e){}
26467         }
26468         this.owner.fireEvent('activate', this);
26469     },
26470
26471     // private
26472     adjustFont: function(btn){
26473         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26474         //if(Roo.isSafari){ // safari
26475         //    adjust *= 2;
26476        // }
26477         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26478         if(Roo.isSafari){ // safari
26479             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26480             v =  (v < 10) ? 10 : v;
26481             v =  (v > 48) ? 48 : v;
26482             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26483             
26484         }
26485         
26486         
26487         v = Math.max(1, v+adjust);
26488         
26489         this.execCmd('FontSize', v  );
26490     },
26491
26492     onEditorEvent : function(e)
26493     {
26494         this.owner.fireEvent('editorevent', this, e);
26495       //  this.updateToolbar();
26496         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26497     },
26498
26499     insertTag : function(tg)
26500     {
26501         // could be a bit smarter... -> wrap the current selected tRoo..
26502         if (tg.toLowerCase() == 'span' ||
26503             tg.toLowerCase() == 'code' ||
26504             tg.toLowerCase() == 'sup' ||
26505             tg.toLowerCase() == 'sub' 
26506             ) {
26507             
26508             range = this.createRange(this.getSelection());
26509             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26510             wrappingNode.appendChild(range.extractContents());
26511             range.insertNode(wrappingNode);
26512
26513             return;
26514             
26515             
26516             
26517         }
26518         this.execCmd("formatblock",   tg);
26519         
26520     },
26521     
26522     insertText : function(txt)
26523     {
26524         
26525         
26526         var range = this.createRange();
26527         range.deleteContents();
26528                //alert(Sender.getAttribute('label'));
26529                
26530         range.insertNode(this.doc.createTextNode(txt));
26531     } ,
26532     
26533      
26534
26535     /**
26536      * Executes a Midas editor command on the editor document and performs necessary focus and
26537      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26538      * @param {String} cmd The Midas command
26539      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26540      */
26541     relayCmd : function(cmd, value){
26542         this.win.focus();
26543         this.execCmd(cmd, value);
26544         this.owner.fireEvent('editorevent', this);
26545         //this.updateToolbar();
26546         this.owner.deferFocus();
26547     },
26548
26549     /**
26550      * Executes a Midas editor command directly on the editor document.
26551      * For visual commands, you should use {@link #relayCmd} instead.
26552      * <b>This should only be called after the editor is initialized.</b>
26553      * @param {String} cmd The Midas command
26554      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26555      */
26556     execCmd : function(cmd, value){
26557         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26558         this.syncValue();
26559     },
26560  
26561  
26562    
26563     /**
26564      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26565      * to insert tRoo.
26566      * @param {String} text | dom node.. 
26567      */
26568     insertAtCursor : function(text)
26569     {
26570         
26571         if(!this.activated){
26572             return;
26573         }
26574         /*
26575         if(Roo.isIE){
26576             this.win.focus();
26577             var r = this.doc.selection.createRange();
26578             if(r){
26579                 r.collapse(true);
26580                 r.pasteHTML(text);
26581                 this.syncValue();
26582                 this.deferFocus();
26583             
26584             }
26585             return;
26586         }
26587         */
26588         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26589             this.win.focus();
26590             
26591             
26592             // from jquery ui (MIT licenced)
26593             var range, node;
26594             var win = this.win;
26595             
26596             if (win.getSelection && win.getSelection().getRangeAt) {
26597                 range = win.getSelection().getRangeAt(0);
26598                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26599                 range.insertNode(node);
26600             } else if (win.document.selection && win.document.selection.createRange) {
26601                 // no firefox support
26602                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26603                 win.document.selection.createRange().pasteHTML(txt);
26604             } else {
26605                 // no firefox support
26606                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26607                 this.execCmd('InsertHTML', txt);
26608             } 
26609             
26610             this.syncValue();
26611             
26612             this.deferFocus();
26613         }
26614     },
26615  // private
26616     mozKeyPress : function(e){
26617         if(e.ctrlKey){
26618             var c = e.getCharCode(), cmd;
26619           
26620             if(c > 0){
26621                 c = String.fromCharCode(c).toLowerCase();
26622                 switch(c){
26623                     case 'b':
26624                         cmd = 'bold';
26625                         break;
26626                     case 'i':
26627                         cmd = 'italic';
26628                         break;
26629                     
26630                     case 'u':
26631                         cmd = 'underline';
26632                         break;
26633                     
26634                     case 'v':
26635                         this.cleanUpPaste.defer(100, this);
26636                         return;
26637                         
26638                 }
26639                 if(cmd){
26640                     this.win.focus();
26641                     this.execCmd(cmd);
26642                     this.deferFocus();
26643                     e.preventDefault();
26644                 }
26645                 
26646             }
26647         }
26648     },
26649
26650     // private
26651     fixKeys : function(){ // load time branching for fastest keydown performance
26652         if(Roo.isIE){
26653             return function(e){
26654                 var k = e.getKey(), r;
26655                 if(k == e.TAB){
26656                     e.stopEvent();
26657                     r = this.doc.selection.createRange();
26658                     if(r){
26659                         r.collapse(true);
26660                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26661                         this.deferFocus();
26662                     }
26663                     return;
26664                 }
26665                 
26666                 if(k == e.ENTER){
26667                     r = this.doc.selection.createRange();
26668                     if(r){
26669                         var target = r.parentElement();
26670                         if(!target || target.tagName.toLowerCase() != 'li'){
26671                             e.stopEvent();
26672                             r.pasteHTML('<br />');
26673                             r.collapse(false);
26674                             r.select();
26675                         }
26676                     }
26677                 }
26678                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26679                     this.cleanUpPaste.defer(100, this);
26680                     return;
26681                 }
26682                 
26683                 
26684             };
26685         }else if(Roo.isOpera){
26686             return function(e){
26687                 var k = e.getKey();
26688                 if(k == e.TAB){
26689                     e.stopEvent();
26690                     this.win.focus();
26691                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26692                     this.deferFocus();
26693                 }
26694                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26695                     this.cleanUpPaste.defer(100, this);
26696                     return;
26697                 }
26698                 
26699             };
26700         }else if(Roo.isSafari){
26701             return function(e){
26702                 var k = e.getKey();
26703                 
26704                 if(k == e.TAB){
26705                     e.stopEvent();
26706                     this.execCmd('InsertText','\t');
26707                     this.deferFocus();
26708                     return;
26709                 }
26710                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26711                     this.cleanUpPaste.defer(100, this);
26712                     return;
26713                 }
26714                 
26715              };
26716         }
26717     }(),
26718     
26719     getAllAncestors: function()
26720     {
26721         var p = this.getSelectedNode();
26722         var a = [];
26723         if (!p) {
26724             a.push(p); // push blank onto stack..
26725             p = this.getParentElement();
26726         }
26727         
26728         
26729         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26730             a.push(p);
26731             p = p.parentNode;
26732         }
26733         a.push(this.doc.body);
26734         return a;
26735     },
26736     lastSel : false,
26737     lastSelNode : false,
26738     
26739     
26740     getSelection : function() 
26741     {
26742         this.assignDocWin();
26743         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26744     },
26745     
26746     getSelectedNode: function() 
26747     {
26748         // this may only work on Gecko!!!
26749         
26750         // should we cache this!!!!
26751         
26752         
26753         
26754          
26755         var range = this.createRange(this.getSelection()).cloneRange();
26756         
26757         if (Roo.isIE) {
26758             var parent = range.parentElement();
26759             while (true) {
26760                 var testRange = range.duplicate();
26761                 testRange.moveToElementText(parent);
26762                 if (testRange.inRange(range)) {
26763                     break;
26764                 }
26765                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26766                     break;
26767                 }
26768                 parent = parent.parentElement;
26769             }
26770             return parent;
26771         }
26772         
26773         // is ancestor a text element.
26774         var ac =  range.commonAncestorContainer;
26775         if (ac.nodeType == 3) {
26776             ac = ac.parentNode;
26777         }
26778         
26779         var ar = ac.childNodes;
26780          
26781         var nodes = [];
26782         var other_nodes = [];
26783         var has_other_nodes = false;
26784         for (var i=0;i<ar.length;i++) {
26785             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26786                 continue;
26787             }
26788             // fullly contained node.
26789             
26790             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26791                 nodes.push(ar[i]);
26792                 continue;
26793             }
26794             
26795             // probably selected..
26796             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26797                 other_nodes.push(ar[i]);
26798                 continue;
26799             }
26800             // outer..
26801             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26802                 continue;
26803             }
26804             
26805             
26806             has_other_nodes = true;
26807         }
26808         if (!nodes.length && other_nodes.length) {
26809             nodes= other_nodes;
26810         }
26811         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26812             return false;
26813         }
26814         
26815         return nodes[0];
26816     },
26817     createRange: function(sel)
26818     {
26819         // this has strange effects when using with 
26820         // top toolbar - not sure if it's a great idea.
26821         //this.editor.contentWindow.focus();
26822         if (typeof sel != "undefined") {
26823             try {
26824                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26825             } catch(e) {
26826                 return this.doc.createRange();
26827             }
26828         } else {
26829             return this.doc.createRange();
26830         }
26831     },
26832     getParentElement: function()
26833     {
26834         
26835         this.assignDocWin();
26836         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26837         
26838         var range = this.createRange(sel);
26839          
26840         try {
26841             var p = range.commonAncestorContainer;
26842             while (p.nodeType == 3) { // text node
26843                 p = p.parentNode;
26844             }
26845             return p;
26846         } catch (e) {
26847             return null;
26848         }
26849     
26850     },
26851     /***
26852      *
26853      * Range intersection.. the hard stuff...
26854      *  '-1' = before
26855      *  '0' = hits..
26856      *  '1' = after.
26857      *         [ -- selected range --- ]
26858      *   [fail]                        [fail]
26859      *
26860      *    basically..
26861      *      if end is before start or  hits it. fail.
26862      *      if start is after end or hits it fail.
26863      *
26864      *   if either hits (but other is outside. - then it's not 
26865      *   
26866      *    
26867      **/
26868     
26869     
26870     // @see http://www.thismuchiknow.co.uk/?p=64.
26871     rangeIntersectsNode : function(range, node)
26872     {
26873         var nodeRange = node.ownerDocument.createRange();
26874         try {
26875             nodeRange.selectNode(node);
26876         } catch (e) {
26877             nodeRange.selectNodeContents(node);
26878         }
26879     
26880         var rangeStartRange = range.cloneRange();
26881         rangeStartRange.collapse(true);
26882     
26883         var rangeEndRange = range.cloneRange();
26884         rangeEndRange.collapse(false);
26885     
26886         var nodeStartRange = nodeRange.cloneRange();
26887         nodeStartRange.collapse(true);
26888     
26889         var nodeEndRange = nodeRange.cloneRange();
26890         nodeEndRange.collapse(false);
26891     
26892         return rangeStartRange.compareBoundaryPoints(
26893                  Range.START_TO_START, nodeEndRange) == -1 &&
26894                rangeEndRange.compareBoundaryPoints(
26895                  Range.START_TO_START, nodeStartRange) == 1;
26896         
26897          
26898     },
26899     rangeCompareNode : function(range, node)
26900     {
26901         var nodeRange = node.ownerDocument.createRange();
26902         try {
26903             nodeRange.selectNode(node);
26904         } catch (e) {
26905             nodeRange.selectNodeContents(node);
26906         }
26907         
26908         
26909         range.collapse(true);
26910     
26911         nodeRange.collapse(true);
26912      
26913         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26914         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26915          
26916         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26917         
26918         var nodeIsBefore   =  ss == 1;
26919         var nodeIsAfter    = ee == -1;
26920         
26921         if (nodeIsBefore && nodeIsAfter) {
26922             return 0; // outer
26923         }
26924         if (!nodeIsBefore && nodeIsAfter) {
26925             return 1; //right trailed.
26926         }
26927         
26928         if (nodeIsBefore && !nodeIsAfter) {
26929             return 2;  // left trailed.
26930         }
26931         // fully contined.
26932         return 3;
26933     },
26934
26935     // private? - in a new class?
26936     cleanUpPaste :  function()
26937     {
26938         // cleans up the whole document..
26939         Roo.log('cleanuppaste');
26940         
26941         this.cleanUpChildren(this.doc.body);
26942         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26943         if (clean != this.doc.body.innerHTML) {
26944             this.doc.body.innerHTML = clean;
26945         }
26946         
26947     },
26948     
26949     cleanWordChars : function(input) {// change the chars to hex code
26950         var he = Roo.HtmlEditorCore;
26951         
26952         var output = input;
26953         Roo.each(he.swapCodes, function(sw) { 
26954             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26955             
26956             output = output.replace(swapper, sw[1]);
26957         });
26958         
26959         return output;
26960     },
26961     
26962     
26963     cleanUpChildren : function (n)
26964     {
26965         if (!n.childNodes.length) {
26966             return;
26967         }
26968         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26969            this.cleanUpChild(n.childNodes[i]);
26970         }
26971     },
26972     
26973     
26974         
26975     
26976     cleanUpChild : function (node)
26977     {
26978         var ed = this;
26979         //console.log(node);
26980         if (node.nodeName == "#text") {
26981             // clean up silly Windows -- stuff?
26982             return; 
26983         }
26984         if (node.nodeName == "#comment") {
26985             if (!this.allowComments) {
26986                 node.parentNode.removeChild(node);
26987             }
26988             // clean up silly Windows -- stuff?
26989             return; 
26990         }
26991         var lcname = node.tagName.toLowerCase();
26992         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26993         // whitelist of tags..
26994         
26995         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26996             // remove node.
26997             node.parentNode.removeChild(node);
26998             return;
26999             
27000         }
27001         
27002         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27003         
27004         // spans with no attributes - just remove them..
27005         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27006             remove_keep_children = true;
27007         }
27008         
27009         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27010         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27011         
27012         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27013         //    remove_keep_children = true;
27014         //}
27015         
27016         if (remove_keep_children) {
27017             this.cleanUpChildren(node);
27018             // inserts everything just before this node...
27019             while (node.childNodes.length) {
27020                 var cn = node.childNodes[0];
27021                 node.removeChild(cn);
27022                 node.parentNode.insertBefore(cn, node);
27023             }
27024             node.parentNode.removeChild(node);
27025             return;
27026         }
27027         
27028         if (!node.attributes || !node.attributes.length) {
27029             
27030           
27031             
27032             
27033             this.cleanUpChildren(node);
27034             return;
27035         }
27036         
27037         function cleanAttr(n,v)
27038         {
27039             
27040             if (v.match(/^\./) || v.match(/^\//)) {
27041                 return;
27042             }
27043             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27044                 return;
27045             }
27046             if (v.match(/^#/)) {
27047                 return;
27048             }
27049             if (v.match(/^\{/)) { // allow template editing.
27050                 return;
27051             }
27052 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27053             node.removeAttribute(n);
27054             
27055         }
27056         
27057         var cwhite = this.cwhite;
27058         var cblack = this.cblack;
27059             
27060         function cleanStyle(n,v)
27061         {
27062             if (v.match(/expression/)) { //XSS?? should we even bother..
27063                 node.removeAttribute(n);
27064                 return;
27065             }
27066             
27067             var parts = v.split(/;/);
27068             var clean = [];
27069             
27070             Roo.each(parts, function(p) {
27071                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27072                 if (!p.length) {
27073                     return true;
27074                 }
27075                 var l = p.split(':').shift().replace(/\s+/g,'');
27076                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27077                 
27078                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27079 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27080                     //node.removeAttribute(n);
27081                     return true;
27082                 }
27083                 //Roo.log()
27084                 // only allow 'c whitelisted system attributes'
27085                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27086 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27087                     //node.removeAttribute(n);
27088                     return true;
27089                 }
27090                 
27091                 
27092                  
27093                 
27094                 clean.push(p);
27095                 return true;
27096             });
27097             if (clean.length) { 
27098                 node.setAttribute(n, clean.join(';'));
27099             } else {
27100                 node.removeAttribute(n);
27101             }
27102             
27103         }
27104         
27105         
27106         for (var i = node.attributes.length-1; i > -1 ; i--) {
27107             var a = node.attributes[i];
27108             //console.log(a);
27109             
27110             if (a.name.toLowerCase().substr(0,2)=='on')  {
27111                 node.removeAttribute(a.name);
27112                 continue;
27113             }
27114             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27115                 node.removeAttribute(a.name);
27116                 continue;
27117             }
27118             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27119                 cleanAttr(a.name,a.value); // fixme..
27120                 continue;
27121             }
27122             if (a.name == 'style') {
27123                 cleanStyle(a.name,a.value);
27124                 continue;
27125             }
27126             /// clean up MS crap..
27127             // tecnically this should be a list of valid class'es..
27128             
27129             
27130             if (a.name == 'class') {
27131                 if (a.value.match(/^Mso/)) {
27132                     node.removeAttribute('class');
27133                 }
27134                 
27135                 if (a.value.match(/^body$/)) {
27136                     node.removeAttribute('class');
27137                 }
27138                 continue;
27139             }
27140             
27141             // style cleanup!?
27142             // class cleanup?
27143             
27144         }
27145         
27146         
27147         this.cleanUpChildren(node);
27148         
27149         
27150     },
27151     
27152     /**
27153      * Clean up MS wordisms...
27154      */
27155     cleanWord : function(node)
27156     {
27157         if (!node) {
27158             this.cleanWord(this.doc.body);
27159             return;
27160         }
27161         
27162         if(
27163                 node.nodeName == 'SPAN' &&
27164                 !node.hasAttributes() &&
27165                 node.childNodes.length == 1 &&
27166                 node.firstChild.nodeName == "#text"  
27167         ) {
27168             var textNode = node.firstChild;
27169             node.removeChild(textNode);
27170             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27171                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27172             }
27173             node.parentNode.insertBefore(textNode, node);
27174             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27175                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27176             }
27177             node.parentNode.removeChild(node);
27178         }
27179         
27180         if (node.nodeName == "#text") {
27181             // clean up silly Windows -- stuff?
27182             return; 
27183         }
27184         if (node.nodeName == "#comment") {
27185             node.parentNode.removeChild(node);
27186             // clean up silly Windows -- stuff?
27187             return; 
27188         }
27189         
27190         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27191             node.parentNode.removeChild(node);
27192             return;
27193         }
27194         //Roo.log(node.tagName);
27195         // remove - but keep children..
27196         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27197             //Roo.log('-- removed');
27198             while (node.childNodes.length) {
27199                 var cn = node.childNodes[0];
27200                 node.removeChild(cn);
27201                 node.parentNode.insertBefore(cn, node);
27202                 // move node to parent - and clean it..
27203                 this.cleanWord(cn);
27204             }
27205             node.parentNode.removeChild(node);
27206             /// no need to iterate chidlren = it's got none..
27207             //this.iterateChildren(node, this.cleanWord);
27208             return;
27209         }
27210         // clean styles
27211         if (node.className.length) {
27212             
27213             var cn = node.className.split(/\W+/);
27214             var cna = [];
27215             Roo.each(cn, function(cls) {
27216                 if (cls.match(/Mso[a-zA-Z]+/)) {
27217                     return;
27218                 }
27219                 cna.push(cls);
27220             });
27221             node.className = cna.length ? cna.join(' ') : '';
27222             if (!cna.length) {
27223                 node.removeAttribute("class");
27224             }
27225         }
27226         
27227         if (node.hasAttribute("lang")) {
27228             node.removeAttribute("lang");
27229         }
27230         
27231         if (node.hasAttribute("style")) {
27232             
27233             var styles = node.getAttribute("style").split(";");
27234             var nstyle = [];
27235             Roo.each(styles, function(s) {
27236                 if (!s.match(/:/)) {
27237                     return;
27238                 }
27239                 var kv = s.split(":");
27240                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27241                     return;
27242                 }
27243                 // what ever is left... we allow.
27244                 nstyle.push(s);
27245             });
27246             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27247             if (!nstyle.length) {
27248                 node.removeAttribute('style');
27249             }
27250         }
27251         this.iterateChildren(node, this.cleanWord);
27252         
27253         
27254         
27255     },
27256     /**
27257      * iterateChildren of a Node, calling fn each time, using this as the scole..
27258      * @param {DomNode} node node to iterate children of.
27259      * @param {Function} fn method of this class to call on each item.
27260      */
27261     iterateChildren : function(node, fn)
27262     {
27263         if (!node.childNodes.length) {
27264                 return;
27265         }
27266         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27267            fn.call(this, node.childNodes[i])
27268         }
27269     },
27270     
27271     
27272     /**
27273      * cleanTableWidths.
27274      *
27275      * Quite often pasting from word etc.. results in tables with column and widths.
27276      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27277      *
27278      */
27279     cleanTableWidths : function(node)
27280     {
27281          
27282          
27283         if (!node) {
27284             this.cleanTableWidths(this.doc.body);
27285             return;
27286         }
27287         
27288         // ignore list...
27289         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27290             return; 
27291         }
27292         Roo.log(node.tagName);
27293         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27294             this.iterateChildren(node, this.cleanTableWidths);
27295             return;
27296         }
27297         if (node.hasAttribute('width')) {
27298             node.removeAttribute('width');
27299         }
27300         
27301          
27302         if (node.hasAttribute("style")) {
27303             // pretty basic...
27304             
27305             var styles = node.getAttribute("style").split(";");
27306             var nstyle = [];
27307             Roo.each(styles, function(s) {
27308                 if (!s.match(/:/)) {
27309                     return;
27310                 }
27311                 var kv = s.split(":");
27312                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27313                     return;
27314                 }
27315                 // what ever is left... we allow.
27316                 nstyle.push(s);
27317             });
27318             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27319             if (!nstyle.length) {
27320                 node.removeAttribute('style');
27321             }
27322         }
27323         
27324         this.iterateChildren(node, this.cleanTableWidths);
27325         
27326         
27327     },
27328     
27329     
27330     
27331     
27332     domToHTML : function(currentElement, depth, nopadtext) {
27333         
27334         depth = depth || 0;
27335         nopadtext = nopadtext || false;
27336     
27337         if (!currentElement) {
27338             return this.domToHTML(this.doc.body);
27339         }
27340         
27341         //Roo.log(currentElement);
27342         var j;
27343         var allText = false;
27344         var nodeName = currentElement.nodeName;
27345         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27346         
27347         if  (nodeName == '#text') {
27348             
27349             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27350         }
27351         
27352         
27353         var ret = '';
27354         if (nodeName != 'BODY') {
27355              
27356             var i = 0;
27357             // Prints the node tagName, such as <A>, <IMG>, etc
27358             if (tagName) {
27359                 var attr = [];
27360                 for(i = 0; i < currentElement.attributes.length;i++) {
27361                     // quoting?
27362                     var aname = currentElement.attributes.item(i).name;
27363                     if (!currentElement.attributes.item(i).value.length) {
27364                         continue;
27365                     }
27366                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27367                 }
27368                 
27369                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27370             } 
27371             else {
27372                 
27373                 // eack
27374             }
27375         } else {
27376             tagName = false;
27377         }
27378         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27379             return ret;
27380         }
27381         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27382             nopadtext = true;
27383         }
27384         
27385         
27386         // Traverse the tree
27387         i = 0;
27388         var currentElementChild = currentElement.childNodes.item(i);
27389         var allText = true;
27390         var innerHTML  = '';
27391         lastnode = '';
27392         while (currentElementChild) {
27393             // Formatting code (indent the tree so it looks nice on the screen)
27394             var nopad = nopadtext;
27395             if (lastnode == 'SPAN') {
27396                 nopad  = true;
27397             }
27398             // text
27399             if  (currentElementChild.nodeName == '#text') {
27400                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27401                 toadd = nopadtext ? toadd : toadd.trim();
27402                 if (!nopad && toadd.length > 80) {
27403                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27404                 }
27405                 innerHTML  += toadd;
27406                 
27407                 i++;
27408                 currentElementChild = currentElement.childNodes.item(i);
27409                 lastNode = '';
27410                 continue;
27411             }
27412             allText = false;
27413             
27414             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27415                 
27416             // Recursively traverse the tree structure of the child node
27417             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27418             lastnode = currentElementChild.nodeName;
27419             i++;
27420             currentElementChild=currentElement.childNodes.item(i);
27421         }
27422         
27423         ret += innerHTML;
27424         
27425         if (!allText) {
27426                 // The remaining code is mostly for formatting the tree
27427             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27428         }
27429         
27430         
27431         if (tagName) {
27432             ret+= "</"+tagName+">";
27433         }
27434         return ret;
27435         
27436     },
27437         
27438     applyBlacklists : function()
27439     {
27440         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27441         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27442         
27443         this.white = [];
27444         this.black = [];
27445         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27446             if (b.indexOf(tag) > -1) {
27447                 return;
27448             }
27449             this.white.push(tag);
27450             
27451         }, this);
27452         
27453         Roo.each(w, function(tag) {
27454             if (b.indexOf(tag) > -1) {
27455                 return;
27456             }
27457             if (this.white.indexOf(tag) > -1) {
27458                 return;
27459             }
27460             this.white.push(tag);
27461             
27462         }, this);
27463         
27464         
27465         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27466             if (w.indexOf(tag) > -1) {
27467                 return;
27468             }
27469             this.black.push(tag);
27470             
27471         }, this);
27472         
27473         Roo.each(b, function(tag) {
27474             if (w.indexOf(tag) > -1) {
27475                 return;
27476             }
27477             if (this.black.indexOf(tag) > -1) {
27478                 return;
27479             }
27480             this.black.push(tag);
27481             
27482         }, this);
27483         
27484         
27485         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27486         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27487         
27488         this.cwhite = [];
27489         this.cblack = [];
27490         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27491             if (b.indexOf(tag) > -1) {
27492                 return;
27493             }
27494             this.cwhite.push(tag);
27495             
27496         }, this);
27497         
27498         Roo.each(w, function(tag) {
27499             if (b.indexOf(tag) > -1) {
27500                 return;
27501             }
27502             if (this.cwhite.indexOf(tag) > -1) {
27503                 return;
27504             }
27505             this.cwhite.push(tag);
27506             
27507         }, this);
27508         
27509         
27510         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27511             if (w.indexOf(tag) > -1) {
27512                 return;
27513             }
27514             this.cblack.push(tag);
27515             
27516         }, this);
27517         
27518         Roo.each(b, function(tag) {
27519             if (w.indexOf(tag) > -1) {
27520                 return;
27521             }
27522             if (this.cblack.indexOf(tag) > -1) {
27523                 return;
27524             }
27525             this.cblack.push(tag);
27526             
27527         }, this);
27528     },
27529     
27530     setStylesheets : function(stylesheets)
27531     {
27532         if(typeof(stylesheets) == 'string'){
27533             Roo.get(this.iframe.contentDocument.head).createChild({
27534                 tag : 'link',
27535                 rel : 'stylesheet',
27536                 type : 'text/css',
27537                 href : stylesheets
27538             });
27539             
27540             return;
27541         }
27542         var _this = this;
27543      
27544         Roo.each(stylesheets, function(s) {
27545             if(!s.length){
27546                 return;
27547             }
27548             
27549             Roo.get(_this.iframe.contentDocument.head).createChild({
27550                 tag : 'link',
27551                 rel : 'stylesheet',
27552                 type : 'text/css',
27553                 href : s
27554             });
27555         });
27556
27557         
27558     },
27559     
27560     removeStylesheets : function()
27561     {
27562         var _this = this;
27563         
27564         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27565             s.remove();
27566         });
27567     },
27568     
27569     setStyle : function(style)
27570     {
27571         Roo.get(this.iframe.contentDocument.head).createChild({
27572             tag : 'style',
27573             type : 'text/css',
27574             html : style
27575         });
27576
27577         return;
27578     }
27579     
27580     // hide stuff that is not compatible
27581     /**
27582      * @event blur
27583      * @hide
27584      */
27585     /**
27586      * @event change
27587      * @hide
27588      */
27589     /**
27590      * @event focus
27591      * @hide
27592      */
27593     /**
27594      * @event specialkey
27595      * @hide
27596      */
27597     /**
27598      * @cfg {String} fieldClass @hide
27599      */
27600     /**
27601      * @cfg {String} focusClass @hide
27602      */
27603     /**
27604      * @cfg {String} autoCreate @hide
27605      */
27606     /**
27607      * @cfg {String} inputType @hide
27608      */
27609     /**
27610      * @cfg {String} invalidClass @hide
27611      */
27612     /**
27613      * @cfg {String} invalidText @hide
27614      */
27615     /**
27616      * @cfg {String} msgFx @hide
27617      */
27618     /**
27619      * @cfg {String} validateOnBlur @hide
27620      */
27621 });
27622
27623 Roo.HtmlEditorCore.white = [
27624         'area', 'br', 'img', 'input', 'hr', 'wbr',
27625         
27626        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27627        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27628        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27629        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27630        'table',   'ul',         'xmp', 
27631        
27632        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27633       'thead',   'tr', 
27634      
27635       'dir', 'menu', 'ol', 'ul', 'dl',
27636        
27637       'embed',  'object'
27638 ];
27639
27640
27641 Roo.HtmlEditorCore.black = [
27642     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27643         'applet', // 
27644         'base',   'basefont', 'bgsound', 'blink',  'body', 
27645         'frame',  'frameset', 'head',    'html',   'ilayer', 
27646         'iframe', 'layer',  'link',     'meta',    'object',   
27647         'script', 'style' ,'title',  'xml' // clean later..
27648 ];
27649 Roo.HtmlEditorCore.clean = [
27650     'script', 'style', 'title', 'xml'
27651 ];
27652 Roo.HtmlEditorCore.remove = [
27653     'font'
27654 ];
27655 // attributes..
27656
27657 Roo.HtmlEditorCore.ablack = [
27658     'on'
27659 ];
27660     
27661 Roo.HtmlEditorCore.aclean = [ 
27662     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27663 ];
27664
27665 // protocols..
27666 Roo.HtmlEditorCore.pwhite= [
27667         'http',  'https',  'mailto'
27668 ];
27669
27670 // white listed style attributes.
27671 Roo.HtmlEditorCore.cwhite= [
27672       //  'text-align', /// default is to allow most things..
27673       
27674          
27675 //        'font-size'//??
27676 ];
27677
27678 // black listed style attributes.
27679 Roo.HtmlEditorCore.cblack= [
27680       //  'font-size' -- this can be set by the project 
27681 ];
27682
27683
27684 Roo.HtmlEditorCore.swapCodes   =[ 
27685     [    8211, "&#8211;" ], 
27686     [    8212, "&#8212;" ], 
27687     [    8216,  "'" ],  
27688     [    8217, "'" ],  
27689     [    8220, '"' ],  
27690     [    8221, '"' ],  
27691     [    8226, "*" ],  
27692     [    8230, "..." ]
27693 ]; 
27694
27695     /*
27696  * - LGPL
27697  *
27698  * HtmlEditor
27699  * 
27700  */
27701
27702 /**
27703  * @class Roo.bootstrap.HtmlEditor
27704  * @extends Roo.bootstrap.TextArea
27705  * Bootstrap HtmlEditor class
27706
27707  * @constructor
27708  * Create a new HtmlEditor
27709  * @param {Object} config The config object
27710  */
27711
27712 Roo.bootstrap.HtmlEditor = function(config){
27713     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27714     if (!this.toolbars) {
27715         this.toolbars = [];
27716     }
27717     
27718     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27719     this.addEvents({
27720             /**
27721              * @event initialize
27722              * Fires when the editor is fully initialized (including the iframe)
27723              * @param {HtmlEditor} this
27724              */
27725             initialize: true,
27726             /**
27727              * @event activate
27728              * Fires when the editor is first receives the focus. Any insertion must wait
27729              * until after this event.
27730              * @param {HtmlEditor} this
27731              */
27732             activate: true,
27733              /**
27734              * @event beforesync
27735              * Fires before the textarea is updated with content from the editor iframe. Return false
27736              * to cancel the sync.
27737              * @param {HtmlEditor} this
27738              * @param {String} html
27739              */
27740             beforesync: true,
27741              /**
27742              * @event beforepush
27743              * Fires before the iframe editor is updated with content from the textarea. Return false
27744              * to cancel the push.
27745              * @param {HtmlEditor} this
27746              * @param {String} html
27747              */
27748             beforepush: true,
27749              /**
27750              * @event sync
27751              * Fires when the textarea is updated with content from the editor iframe.
27752              * @param {HtmlEditor} this
27753              * @param {String} html
27754              */
27755             sync: true,
27756              /**
27757              * @event push
27758              * Fires when the iframe editor is updated with content from the textarea.
27759              * @param {HtmlEditor} this
27760              * @param {String} html
27761              */
27762             push: true,
27763              /**
27764              * @event editmodechange
27765              * Fires when the editor switches edit modes
27766              * @param {HtmlEditor} this
27767              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27768              */
27769             editmodechange: true,
27770             /**
27771              * @event editorevent
27772              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27773              * @param {HtmlEditor} this
27774              */
27775             editorevent: true,
27776             /**
27777              * @event firstfocus
27778              * Fires when on first focus - needed by toolbars..
27779              * @param {HtmlEditor} this
27780              */
27781             firstfocus: true,
27782             /**
27783              * @event autosave
27784              * Auto save the htmlEditor value as a file into Events
27785              * @param {HtmlEditor} this
27786              */
27787             autosave: true,
27788             /**
27789              * @event savedpreview
27790              * preview the saved version of htmlEditor
27791              * @param {HtmlEditor} this
27792              */
27793             savedpreview: true
27794         });
27795 };
27796
27797
27798 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27799     
27800     
27801       /**
27802      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27803      */
27804     toolbars : false,
27805     
27806      /**
27807     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27808     */
27809     btns : [],
27810    
27811      /**
27812      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27813      *                        Roo.resizable.
27814      */
27815     resizable : false,
27816      /**
27817      * @cfg {Number} height (in pixels)
27818      */   
27819     height: 300,
27820    /**
27821      * @cfg {Number} width (in pixels)
27822      */   
27823     width: false,
27824     
27825     /**
27826      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27827      * 
27828      */
27829     stylesheets: false,
27830     
27831     // id of frame..
27832     frameId: false,
27833     
27834     // private properties
27835     validationEvent : false,
27836     deferHeight: true,
27837     initialized : false,
27838     activated : false,
27839     
27840     onFocus : Roo.emptyFn,
27841     iframePad:3,
27842     hideMode:'offsets',
27843     
27844     tbContainer : false,
27845     
27846     bodyCls : '',
27847     
27848     toolbarContainer :function() {
27849         return this.wrap.select('.x-html-editor-tb',true).first();
27850     },
27851
27852     /**
27853      * Protected method that will not generally be called directly. It
27854      * is called when the editor creates its toolbar. Override this method if you need to
27855      * add custom toolbar buttons.
27856      * @param {HtmlEditor} editor
27857      */
27858     createToolbar : function(){
27859         Roo.log('renewing');
27860         Roo.log("create toolbars");
27861         
27862         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27863         this.toolbars[0].render(this.toolbarContainer());
27864         
27865         return;
27866         
27867 //        if (!editor.toolbars || !editor.toolbars.length) {
27868 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27869 //        }
27870 //        
27871 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27872 //            editor.toolbars[i] = Roo.factory(
27873 //                    typeof(editor.toolbars[i]) == 'string' ?
27874 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27875 //                Roo.bootstrap.HtmlEditor);
27876 //            editor.toolbars[i].init(editor);
27877 //        }
27878     },
27879
27880      
27881     // private
27882     onRender : function(ct, position)
27883     {
27884        // Roo.log("Call onRender: " + this.xtype);
27885         var _t = this;
27886         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27887       
27888         this.wrap = this.inputEl().wrap({
27889             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27890         });
27891         
27892         this.editorcore.onRender(ct, position);
27893          
27894         if (this.resizable) {
27895             this.resizeEl = new Roo.Resizable(this.wrap, {
27896                 pinned : true,
27897                 wrap: true,
27898                 dynamic : true,
27899                 minHeight : this.height,
27900                 height: this.height,
27901                 handles : this.resizable,
27902                 width: this.width,
27903                 listeners : {
27904                     resize : function(r, w, h) {
27905                         _t.onResize(w,h); // -something
27906                     }
27907                 }
27908             });
27909             
27910         }
27911         this.createToolbar(this);
27912        
27913         
27914         if(!this.width && this.resizable){
27915             this.setSize(this.wrap.getSize());
27916         }
27917         if (this.resizeEl) {
27918             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27919             // should trigger onReize..
27920         }
27921         
27922     },
27923
27924     // private
27925     onResize : function(w, h)
27926     {
27927         Roo.log('resize: ' +w + ',' + h );
27928         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27929         var ew = false;
27930         var eh = false;
27931         
27932         if(this.inputEl() ){
27933             if(typeof w == 'number'){
27934                 var aw = w - this.wrap.getFrameWidth('lr');
27935                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27936                 ew = aw;
27937             }
27938             if(typeof h == 'number'){
27939                  var tbh = -11;  // fixme it needs to tool bar size!
27940                 for (var i =0; i < this.toolbars.length;i++) {
27941                     // fixme - ask toolbars for heights?
27942                     tbh += this.toolbars[i].el.getHeight();
27943                     //if (this.toolbars[i].footer) {
27944                     //    tbh += this.toolbars[i].footer.el.getHeight();
27945                     //}
27946                 }
27947               
27948                 
27949                 
27950                 
27951                 
27952                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27953                 ah -= 5; // knock a few pixes off for look..
27954                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27955                 var eh = ah;
27956             }
27957         }
27958         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27959         this.editorcore.onResize(ew,eh);
27960         
27961     },
27962
27963     /**
27964      * Toggles the editor between standard and source edit mode.
27965      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27966      */
27967     toggleSourceEdit : function(sourceEditMode)
27968     {
27969         this.editorcore.toggleSourceEdit(sourceEditMode);
27970         
27971         if(this.editorcore.sourceEditMode){
27972             Roo.log('editor - showing textarea');
27973             
27974 //            Roo.log('in');
27975 //            Roo.log(this.syncValue());
27976             this.syncValue();
27977             this.inputEl().removeClass(['hide', 'x-hidden']);
27978             this.inputEl().dom.removeAttribute('tabIndex');
27979             this.inputEl().focus();
27980         }else{
27981             Roo.log('editor - hiding textarea');
27982 //            Roo.log('out')
27983 //            Roo.log(this.pushValue()); 
27984             this.pushValue();
27985             
27986             this.inputEl().addClass(['hide', 'x-hidden']);
27987             this.inputEl().dom.setAttribute('tabIndex', -1);
27988             //this.deferFocus();
27989         }
27990          
27991         if(this.resizable){
27992             this.setSize(this.wrap.getSize());
27993         }
27994         
27995         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27996     },
27997  
27998     // private (for BoxComponent)
27999     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28000
28001     // private (for BoxComponent)
28002     getResizeEl : function(){
28003         return this.wrap;
28004     },
28005
28006     // private (for BoxComponent)
28007     getPositionEl : function(){
28008         return this.wrap;
28009     },
28010
28011     // private
28012     initEvents : function(){
28013         this.originalValue = this.getValue();
28014     },
28015
28016 //    /**
28017 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28018 //     * @method
28019 //     */
28020 //    markInvalid : Roo.emptyFn,
28021 //    /**
28022 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28023 //     * @method
28024 //     */
28025 //    clearInvalid : Roo.emptyFn,
28026
28027     setValue : function(v){
28028         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
28029         this.editorcore.pushValue();
28030     },
28031
28032      
28033     // private
28034     deferFocus : function(){
28035         this.focus.defer(10, this);
28036     },
28037
28038     // doc'ed in Field
28039     focus : function(){
28040         this.editorcore.focus();
28041         
28042     },
28043       
28044
28045     // private
28046     onDestroy : function(){
28047         
28048         
28049         
28050         if(this.rendered){
28051             
28052             for (var i =0; i < this.toolbars.length;i++) {
28053                 // fixme - ask toolbars for heights?
28054                 this.toolbars[i].onDestroy();
28055             }
28056             
28057             this.wrap.dom.innerHTML = '';
28058             this.wrap.remove();
28059         }
28060     },
28061
28062     // private
28063     onFirstFocus : function(){
28064         //Roo.log("onFirstFocus");
28065         this.editorcore.onFirstFocus();
28066          for (var i =0; i < this.toolbars.length;i++) {
28067             this.toolbars[i].onFirstFocus();
28068         }
28069         
28070     },
28071     
28072     // private
28073     syncValue : function()
28074     {   
28075         this.editorcore.syncValue();
28076     },
28077     
28078     pushValue : function()
28079     {   
28080         this.editorcore.pushValue();
28081     }
28082      
28083     
28084     // hide stuff that is not compatible
28085     /**
28086      * @event blur
28087      * @hide
28088      */
28089     /**
28090      * @event change
28091      * @hide
28092      */
28093     /**
28094      * @event focus
28095      * @hide
28096      */
28097     /**
28098      * @event specialkey
28099      * @hide
28100      */
28101     /**
28102      * @cfg {String} fieldClass @hide
28103      */
28104     /**
28105      * @cfg {String} focusClass @hide
28106      */
28107     /**
28108      * @cfg {String} autoCreate @hide
28109      */
28110     /**
28111      * @cfg {String} inputType @hide
28112      */
28113      
28114     /**
28115      * @cfg {String} invalidText @hide
28116      */
28117     /**
28118      * @cfg {String} msgFx @hide
28119      */
28120     /**
28121      * @cfg {String} validateOnBlur @hide
28122      */
28123 });
28124  
28125     
28126    
28127    
28128    
28129       
28130 Roo.namespace('Roo.bootstrap.htmleditor');
28131 /**
28132  * @class Roo.bootstrap.HtmlEditorToolbar1
28133  * @extends Roo.bootstrap.nav.Simplebar
28134  * Basic Toolbar
28135  * 
28136  * @example
28137  * Usage:
28138  *
28139  new Roo.bootstrap.HtmlEditor({
28140     ....
28141     toolbars : [
28142         new Roo.bootstrap.HtmlEditorToolbar1({
28143             disable : { fonts: 1 , format: 1, ..., ... , ...],
28144             btns : [ .... ]
28145         })
28146     }
28147      
28148  * 
28149  * @cfg {Object} disable List of elements to disable..
28150  * @cfg {Array} btns List of additional buttons.
28151  * 
28152  * 
28153  * NEEDS Extra CSS? 
28154  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28155  */
28156  
28157 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
28158 {
28159     
28160     Roo.apply(this, config);
28161     
28162     // default disabled, based on 'good practice'..
28163     this.disable = this.disable || {};
28164     Roo.applyIf(this.disable, {
28165         fontSize : true,
28166         colors : true,
28167         specialElements : true
28168     });
28169     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
28170     
28171     this.editor = config.editor;
28172     this.editorcore = config.editor.editorcore;
28173     
28174     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28175     
28176     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28177     // dont call parent... till later.
28178 }
28179 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28180      
28181     bar : true,
28182     
28183     editor : false,
28184     editorcore : false,
28185     
28186     
28187     formats : [
28188         "p" ,  
28189         "h1","h2","h3","h4","h5","h6", 
28190         "pre", "code", 
28191         "abbr", "acronym", "address", "cite", "samp", "var",
28192         'div','span'
28193     ],
28194     
28195     onRender : function(ct, position)
28196     {
28197        // Roo.log("Call onRender: " + this.xtype);
28198         
28199        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
28200        Roo.log(this.el);
28201        this.el.dom.style.marginBottom = '0';
28202        var _this = this;
28203        var editorcore = this.editorcore;
28204        var editor= this.editor;
28205        
28206        var children = [];
28207        var btn = function(id,cmd , toggle, handler, html){
28208        
28209             var  event = toggle ? 'toggle' : 'click';
28210        
28211             var a = {
28212                 size : 'sm',
28213                 xtype: 'Button',
28214                 xns: Roo.bootstrap,
28215                 //glyphicon : id,
28216                 fa: id,
28217                 cmd : id || cmd,
28218                 enableToggle:toggle !== false,
28219                 html : html || '',
28220                 pressed : toggle ? false : null,
28221                 listeners : {}
28222             };
28223             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28224                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28225             };
28226             children.push(a);
28227             return a;
28228        }
28229        
28230     //    var cb_box = function...
28231         
28232         var style = {
28233                 xtype: 'Button',
28234                 size : 'sm',
28235                 xns: Roo.bootstrap,
28236                 fa : 'font',
28237                 //html : 'submit'
28238                 menu : {
28239                     xtype: 'Menu',
28240                     xns: Roo.bootstrap,
28241                     items:  []
28242                 }
28243         };
28244         Roo.each(this.formats, function(f) {
28245             style.menu.items.push({
28246                 xtype :'MenuItem',
28247                 xns: Roo.bootstrap,
28248                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28249                 tagname : f,
28250                 listeners : {
28251                     click : function()
28252                     {
28253                         editorcore.insertTag(this.tagname);
28254                         editor.focus();
28255                     }
28256                 }
28257                 
28258             });
28259         });
28260         children.push(style);   
28261         
28262         btn('bold',false,true);
28263         btn('italic',false,true);
28264         btn('align-left', 'justifyleft',true);
28265         btn('align-center', 'justifycenter',true);
28266         btn('align-right' , 'justifyright',true);
28267         btn('link', false, false, function(btn) {
28268             //Roo.log("create link?");
28269             var url = prompt(this.createLinkText, this.defaultLinkValue);
28270             if(url && url != 'http:/'+'/'){
28271                 this.editorcore.relayCmd('createlink', url);
28272             }
28273         }),
28274         btn('list','insertunorderedlist',true);
28275         btn('pencil', false,true, function(btn){
28276                 Roo.log(this);
28277                 this.toggleSourceEdit(btn.pressed);
28278         });
28279         
28280         if (this.editor.btns.length > 0) {
28281             for (var i = 0; i<this.editor.btns.length; i++) {
28282                 children.push(this.editor.btns[i]);
28283             }
28284         }
28285         
28286         /*
28287         var cog = {
28288                 xtype: 'Button',
28289                 size : 'sm',
28290                 xns: Roo.bootstrap,
28291                 glyphicon : 'cog',
28292                 //html : 'submit'
28293                 menu : {
28294                     xtype: 'Menu',
28295                     xns: Roo.bootstrap,
28296                     items:  []
28297                 }
28298         };
28299         
28300         cog.menu.items.push({
28301             xtype :'MenuItem',
28302             xns: Roo.bootstrap,
28303             html : Clean styles,
28304             tagname : f,
28305             listeners : {
28306                 click : function()
28307                 {
28308                     editorcore.insertTag(this.tagname);
28309                     editor.focus();
28310                 }
28311             }
28312             
28313         });
28314        */
28315         
28316          
28317        this.xtype = 'NavSimplebar';
28318         
28319         for(var i=0;i< children.length;i++) {
28320             
28321             this.buttons.add(this.addxtypeChild(children[i]));
28322             
28323         }
28324         
28325         editor.on('editorevent', this.updateToolbar, this);
28326     },
28327     onBtnClick : function(id)
28328     {
28329        this.editorcore.relayCmd(id);
28330        this.editorcore.focus();
28331     },
28332     
28333     /**
28334      * Protected method that will not generally be called directly. It triggers
28335      * a toolbar update by reading the markup state of the current selection in the editor.
28336      */
28337     updateToolbar: function(){
28338
28339         if(!this.editorcore.activated){
28340             this.editor.onFirstFocus(); // is this neeed?
28341             return;
28342         }
28343
28344         var btns = this.buttons; 
28345         var doc = this.editorcore.doc;
28346         btns.get('bold').setActive(doc.queryCommandState('bold'));
28347         btns.get('italic').setActive(doc.queryCommandState('italic'));
28348         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28349         
28350         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28351         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28352         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28353         
28354         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28355         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28356          /*
28357         
28358         var ans = this.editorcore.getAllAncestors();
28359         if (this.formatCombo) {
28360             
28361             
28362             var store = this.formatCombo.store;
28363             this.formatCombo.setValue("");
28364             for (var i =0; i < ans.length;i++) {
28365                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28366                     // select it..
28367                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28368                     break;
28369                 }
28370             }
28371         }
28372         
28373         
28374         
28375         // hides menus... - so this cant be on a menu...
28376         Roo.bootstrap.MenuMgr.hideAll();
28377         */
28378         Roo.bootstrap.menu.Manager.hideAll();
28379         //this.editorsyncValue();
28380     },
28381     onFirstFocus: function() {
28382         this.buttons.each(function(item){
28383            item.enable();
28384         });
28385     },
28386     toggleSourceEdit : function(sourceEditMode){
28387         
28388           
28389         if(sourceEditMode){
28390             Roo.log("disabling buttons");
28391            this.buttons.each( function(item){
28392                 if(item.cmd != 'pencil'){
28393                     item.disable();
28394                 }
28395             });
28396           
28397         }else{
28398             Roo.log("enabling buttons");
28399             if(this.editorcore.initialized){
28400                 this.buttons.each( function(item){
28401                     item.enable();
28402                 });
28403             }
28404             
28405         }
28406         Roo.log("calling toggole on editor");
28407         // tell the editor that it's been pressed..
28408         this.editor.toggleSourceEdit(sourceEditMode);
28409        
28410     }
28411 });
28412
28413
28414
28415
28416  
28417 /*
28418  * - LGPL
28419  */
28420
28421 /**
28422  * @class Roo.bootstrap.Markdown
28423  * @extends Roo.bootstrap.TextArea
28424  * Bootstrap Showdown editable area
28425  * @cfg {string} content
28426  * 
28427  * @constructor
28428  * Create a new Showdown
28429  */
28430
28431 Roo.bootstrap.Markdown = function(config){
28432     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28433    
28434 };
28435
28436 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28437     
28438     editing :false,
28439     
28440     initEvents : function()
28441     {
28442         
28443         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28444         this.markdownEl = this.el.createChild({
28445             cls : 'roo-markdown-area'
28446         });
28447         this.inputEl().addClass('d-none');
28448         if (this.getValue() == '') {
28449             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28450             
28451         } else {
28452             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28453         }
28454         this.markdownEl.on('click', this.toggleTextEdit, this);
28455         this.on('blur', this.toggleTextEdit, this);
28456         this.on('specialkey', this.resizeTextArea, this);
28457     },
28458     
28459     toggleTextEdit : function()
28460     {
28461         var sh = this.markdownEl.getHeight();
28462         this.inputEl().addClass('d-none');
28463         this.markdownEl.addClass('d-none');
28464         if (!this.editing) {
28465             // show editor?
28466             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28467             this.inputEl().removeClass('d-none');
28468             this.inputEl().focus();
28469             this.editing = true;
28470             return;
28471         }
28472         // show showdown...
28473         this.updateMarkdown();
28474         this.markdownEl.removeClass('d-none');
28475         this.editing = false;
28476         return;
28477     },
28478     updateMarkdown : function()
28479     {
28480         if (this.getValue() == '') {
28481             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28482             return;
28483         }
28484  
28485         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28486     },
28487     
28488     resizeTextArea: function () {
28489         
28490         var sh = 100;
28491         Roo.log([sh, this.getValue().split("\n").length * 30]);
28492         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28493     },
28494     setValue : function(val)
28495     {
28496         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28497         if (!this.editing) {
28498             this.updateMarkdown();
28499         }
28500         
28501     },
28502     focus : function()
28503     {
28504         if (!this.editing) {
28505             this.toggleTextEdit();
28506         }
28507         
28508     }
28509
28510
28511 });/*
28512  * Based on:
28513  * Ext JS Library 1.1.1
28514  * Copyright(c) 2006-2007, Ext JS, LLC.
28515  *
28516  * Originally Released Under LGPL - original licence link has changed is not relivant.
28517  *
28518  * Fork - LGPL
28519  * <script type="text/javascript">
28520  */
28521  
28522 /**
28523  * @class Roo.bootstrap.PagingToolbar
28524  * @extends Roo.bootstrap.nav.Simplebar
28525  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28526  * @constructor
28527  * Create a new PagingToolbar
28528  * @param {Object} config The config object
28529  * @param {Roo.data.Store} store
28530  */
28531 Roo.bootstrap.PagingToolbar = function(config)
28532 {
28533     // old args format still supported... - xtype is prefered..
28534         // created from xtype...
28535     
28536     this.ds = config.dataSource;
28537     
28538     if (config.store && !this.ds) {
28539         this.store= Roo.factory(config.store, Roo.data);
28540         this.ds = this.store;
28541         this.ds.xmodule = this.xmodule || false;
28542     }
28543     
28544     this.toolbarItems = [];
28545     if (config.items) {
28546         this.toolbarItems = config.items;
28547     }
28548     
28549     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28550     
28551     this.cursor = 0;
28552     
28553     if (this.ds) { 
28554         this.bind(this.ds);
28555     }
28556     
28557     if (Roo.bootstrap.version == 4) {
28558         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28559     } else {
28560         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28561     }
28562     
28563 };
28564
28565 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28566     /**
28567      * @cfg {Roo.bootstrap.Button} buttons[]
28568      * Buttons for the toolbar
28569      */
28570      /**
28571      * @cfg {Roo.data.Store} store
28572      * The underlying data store providing the paged data
28573      */
28574     /**
28575      * @cfg {String/HTMLElement/Element} container
28576      * container The id or element that will contain the toolbar
28577      */
28578     /**
28579      * @cfg {Boolean} displayInfo
28580      * True to display the displayMsg (defaults to false)
28581      */
28582     /**
28583      * @cfg {Number} pageSize
28584      * The number of records to display per page (defaults to 20)
28585      */
28586     pageSize: 20,
28587     /**
28588      * @cfg {String} displayMsg
28589      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28590      */
28591     displayMsg : 'Displaying {0} - {1} of {2}',
28592     /**
28593      * @cfg {String} emptyMsg
28594      * The message to display when no records are found (defaults to "No data to display")
28595      */
28596     emptyMsg : 'No data to display',
28597     /**
28598      * Customizable piece of the default paging text (defaults to "Page")
28599      * @type String
28600      */
28601     beforePageText : "Page",
28602     /**
28603      * Customizable piece of the default paging text (defaults to "of %0")
28604      * @type String
28605      */
28606     afterPageText : "of {0}",
28607     /**
28608      * Customizable piece of the default paging text (defaults to "First Page")
28609      * @type String
28610      */
28611     firstText : "First Page",
28612     /**
28613      * Customizable piece of the default paging text (defaults to "Previous Page")
28614      * @type String
28615      */
28616     prevText : "Previous Page",
28617     /**
28618      * Customizable piece of the default paging text (defaults to "Next Page")
28619      * @type String
28620      */
28621     nextText : "Next Page",
28622     /**
28623      * Customizable piece of the default paging text (defaults to "Last Page")
28624      * @type String
28625      */
28626     lastText : "Last Page",
28627     /**
28628      * Customizable piece of the default paging text (defaults to "Refresh")
28629      * @type String
28630      */
28631     refreshText : "Refresh",
28632
28633     buttons : false,
28634     // private
28635     onRender : function(ct, position) 
28636     {
28637         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28638         this.navgroup.parentId = this.id;
28639         this.navgroup.onRender(this.el, null);
28640         // add the buttons to the navgroup
28641         
28642         if(this.displayInfo){
28643             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28644             this.displayEl = this.el.select('.x-paging-info', true).first();
28645 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28646 //            this.displayEl = navel.el.select('span',true).first();
28647         }
28648         
28649         var _this = this;
28650         
28651         if(this.buttons){
28652             Roo.each(_this.buttons, function(e){ // this might need to use render????
28653                Roo.factory(e).render(_this.el);
28654             });
28655         }
28656             
28657         Roo.each(_this.toolbarItems, function(e) {
28658             _this.navgroup.addItem(e);
28659         });
28660         
28661         
28662         this.first = this.navgroup.addItem({
28663             tooltip: this.firstText,
28664             cls: "prev btn-outline-secondary",
28665             html : ' <i class="fa fa-step-backward"></i>',
28666             disabled: true,
28667             preventDefault: true,
28668             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28669         });
28670         
28671         this.prev =  this.navgroup.addItem({
28672             tooltip: this.prevText,
28673             cls: "prev btn-outline-secondary",
28674             html : ' <i class="fa fa-backward"></i>',
28675             disabled: true,
28676             preventDefault: true,
28677             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28678         });
28679     //this.addSeparator();
28680         
28681         
28682         var field = this.navgroup.addItem( {
28683             tagtype : 'span',
28684             cls : 'x-paging-position  btn-outline-secondary',
28685              disabled: true,
28686             html : this.beforePageText  +
28687                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28688                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28689          } ); //?? escaped?
28690         
28691         this.field = field.el.select('input', true).first();
28692         this.field.on("keydown", this.onPagingKeydown, this);
28693         this.field.on("focus", function(){this.dom.select();});
28694     
28695     
28696         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28697         //this.field.setHeight(18);
28698         //this.addSeparator();
28699         this.next = this.navgroup.addItem({
28700             tooltip: this.nextText,
28701             cls: "next btn-outline-secondary",
28702             html : ' <i class="fa fa-forward"></i>',
28703             disabled: true,
28704             preventDefault: true,
28705             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28706         });
28707         this.last = this.navgroup.addItem({
28708             tooltip: this.lastText,
28709             html : ' <i class="fa fa-step-forward"></i>',
28710             cls: "next btn-outline-secondary",
28711             disabled: true,
28712             preventDefault: true,
28713             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28714         });
28715     //this.addSeparator();
28716         this.loading = this.navgroup.addItem({
28717             tooltip: this.refreshText,
28718             cls: "btn-outline-secondary",
28719             html : ' <i class="fa fa-refresh"></i>',
28720             preventDefault: true,
28721             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28722         });
28723         
28724     },
28725
28726     // private
28727     updateInfo : function(){
28728         if(this.displayEl){
28729             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28730             var msg = count == 0 ?
28731                 this.emptyMsg :
28732                 String.format(
28733                     this.displayMsg,
28734                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28735                 );
28736             this.displayEl.update(msg);
28737         }
28738     },
28739
28740     // private
28741     onLoad : function(ds, r, o)
28742     {
28743         this.cursor = o.params && o.params.start ? o.params.start : 0;
28744         
28745         var d = this.getPageData(),
28746             ap = d.activePage,
28747             ps = d.pages;
28748         
28749         
28750         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28751         this.field.dom.value = ap;
28752         this.first.setDisabled(ap == 1);
28753         this.prev.setDisabled(ap == 1);
28754         this.next.setDisabled(ap == ps);
28755         this.last.setDisabled(ap == ps);
28756         this.loading.enable();
28757         this.updateInfo();
28758     },
28759
28760     // private
28761     getPageData : function(){
28762         var total = this.ds.getTotalCount();
28763         return {
28764             total : total,
28765             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28766             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28767         };
28768     },
28769
28770     // private
28771     onLoadError : function(){
28772         this.loading.enable();
28773     },
28774
28775     // private
28776     onPagingKeydown : function(e){
28777         var k = e.getKey();
28778         var d = this.getPageData();
28779         if(k == e.RETURN){
28780             var v = this.field.dom.value, pageNum;
28781             if(!v || isNaN(pageNum = parseInt(v, 10))){
28782                 this.field.dom.value = d.activePage;
28783                 return;
28784             }
28785             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28786             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28787             e.stopEvent();
28788         }
28789         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))
28790         {
28791           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28792           this.field.dom.value = pageNum;
28793           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28794           e.stopEvent();
28795         }
28796         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28797         {
28798           var v = this.field.dom.value, pageNum; 
28799           var increment = (e.shiftKey) ? 10 : 1;
28800           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28801                 increment *= -1;
28802           }
28803           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28804             this.field.dom.value = d.activePage;
28805             return;
28806           }
28807           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28808           {
28809             this.field.dom.value = parseInt(v, 10) + increment;
28810             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28811             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28812           }
28813           e.stopEvent();
28814         }
28815     },
28816
28817     // private
28818     beforeLoad : function(){
28819         if(this.loading){
28820             this.loading.disable();
28821         }
28822     },
28823
28824     // private
28825     onClick : function(which){
28826         
28827         var ds = this.ds;
28828         if (!ds) {
28829             return;
28830         }
28831         
28832         switch(which){
28833             case "first":
28834                 ds.load({params:{start: 0, limit: this.pageSize}});
28835             break;
28836             case "prev":
28837                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28838             break;
28839             case "next":
28840                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28841             break;
28842             case "last":
28843                 var total = ds.getTotalCount();
28844                 var extra = total % this.pageSize;
28845                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28846                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28847             break;
28848             case "refresh":
28849                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28850             break;
28851         }
28852     },
28853
28854     /**
28855      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28856      * @param {Roo.data.Store} store The data store to unbind
28857      */
28858     unbind : function(ds){
28859         ds.un("beforeload", this.beforeLoad, this);
28860         ds.un("load", this.onLoad, this);
28861         ds.un("loadexception", this.onLoadError, this);
28862         ds.un("remove", this.updateInfo, this);
28863         ds.un("add", this.updateInfo, this);
28864         this.ds = undefined;
28865     },
28866
28867     /**
28868      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28869      * @param {Roo.data.Store} store The data store to bind
28870      */
28871     bind : function(ds){
28872         ds.on("beforeload", this.beforeLoad, this);
28873         ds.on("load", this.onLoad, this);
28874         ds.on("loadexception", this.onLoadError, this);
28875         ds.on("remove", this.updateInfo, this);
28876         ds.on("add", this.updateInfo, this);
28877         this.ds = ds;
28878     }
28879 });/*
28880  * - LGPL
28881  *
28882  * element
28883  * 
28884  */
28885
28886 /**
28887  * @class Roo.bootstrap.MessageBar
28888  * @extends Roo.bootstrap.Component
28889  * Bootstrap MessageBar class
28890  * @cfg {String} html contents of the MessageBar
28891  * @cfg {String} weight (info | success | warning | danger) default info
28892  * @cfg {String} beforeClass insert the bar before the given class
28893  * @cfg {Boolean} closable (true | false) default false
28894  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28895  * 
28896  * @constructor
28897  * Create a new Element
28898  * @param {Object} config The config object
28899  */
28900
28901 Roo.bootstrap.MessageBar = function(config){
28902     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28903 };
28904
28905 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28906     
28907     html: '',
28908     weight: 'info',
28909     closable: false,
28910     fixed: false,
28911     beforeClass: 'bootstrap-sticky-wrap',
28912     
28913     getAutoCreate : function(){
28914         
28915         var cfg = {
28916             tag: 'div',
28917             cls: 'alert alert-dismissable alert-' + this.weight,
28918             cn: [
28919                 {
28920                     tag: 'span',
28921                     cls: 'message',
28922                     html: this.html || ''
28923                 }
28924             ]
28925         };
28926         
28927         if(this.fixed){
28928             cfg.cls += ' alert-messages-fixed';
28929         }
28930         
28931         if(this.closable){
28932             cfg.cn.push({
28933                 tag: 'button',
28934                 cls: 'close',
28935                 html: 'x'
28936             });
28937         }
28938         
28939         return cfg;
28940     },
28941     
28942     onRender : function(ct, position)
28943     {
28944         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28945         
28946         if(!this.el){
28947             var cfg = Roo.apply({},  this.getAutoCreate());
28948             cfg.id = Roo.id();
28949             
28950             if (this.cls) {
28951                 cfg.cls += ' ' + this.cls;
28952             }
28953             if (this.style) {
28954                 cfg.style = this.style;
28955             }
28956             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28957             
28958             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28959         }
28960         
28961         this.el.select('>button.close').on('click', this.hide, this);
28962         
28963     },
28964     
28965     show : function()
28966     {
28967         if (!this.rendered) {
28968             this.render();
28969         }
28970         
28971         this.el.show();
28972         
28973         this.fireEvent('show', this);
28974         
28975     },
28976     
28977     hide : function()
28978     {
28979         if (!this.rendered) {
28980             this.render();
28981         }
28982         
28983         this.el.hide();
28984         
28985         this.fireEvent('hide', this);
28986     },
28987     
28988     update : function()
28989     {
28990 //        var e = this.el.dom.firstChild;
28991 //        
28992 //        if(this.closable){
28993 //            e = e.nextSibling;
28994 //        }
28995 //        
28996 //        e.data = this.html || '';
28997
28998         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28999     }
29000    
29001 });
29002
29003  
29004
29005      /*
29006  * - LGPL
29007  *
29008  * Graph
29009  * 
29010  */
29011
29012
29013 /**
29014  * @class Roo.bootstrap.Graph
29015  * @extends Roo.bootstrap.Component
29016  * Bootstrap Graph class
29017 > Prameters
29018  -sm {number} sm 4
29019  -md {number} md 5
29020  @cfg {String} graphtype  bar | vbar | pie
29021  @cfg {number} g_x coodinator | centre x (pie)
29022  @cfg {number} g_y coodinator | centre y (pie)
29023  @cfg {number} g_r radius (pie)
29024  @cfg {number} g_height height of the chart (respected by all elements in the set)
29025  @cfg {number} g_width width of the chart (respected by all elements in the set)
29026  @cfg {Object} title The title of the chart
29027     
29028  -{Array}  values
29029  -opts (object) options for the chart 
29030      o {
29031      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29032      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29033      o vgutter (number)
29034      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.
29035      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29036      o to
29037      o stretch (boolean)
29038      o }
29039  -opts (object) options for the pie
29040      o{
29041      o cut
29042      o startAngle (number)
29043      o endAngle (number)
29044      } 
29045  *
29046  * @constructor
29047  * Create a new Input
29048  * @param {Object} config The config object
29049  */
29050
29051 Roo.bootstrap.Graph = function(config){
29052     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29053     
29054     this.addEvents({
29055         // img events
29056         /**
29057          * @event click
29058          * The img click event for the img.
29059          * @param {Roo.EventObject} e
29060          */
29061         "click" : true
29062     });
29063 };
29064
29065 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29066     
29067     sm: 4,
29068     md: 5,
29069     graphtype: 'bar',
29070     g_height: 250,
29071     g_width: 400,
29072     g_x: 50,
29073     g_y: 50,
29074     g_r: 30,
29075     opts:{
29076         //g_colors: this.colors,
29077         g_type: 'soft',
29078         g_gutter: '20%'
29079
29080     },
29081     title : false,
29082
29083     getAutoCreate : function(){
29084         
29085         var cfg = {
29086             tag: 'div',
29087             html : null
29088         };
29089         
29090         
29091         return  cfg;
29092     },
29093
29094     onRender : function(ct,position){
29095         
29096         
29097         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29098         
29099         if (typeof(Raphael) == 'undefined') {
29100             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29101             return;
29102         }
29103         
29104         this.raphael = Raphael(this.el.dom);
29105         
29106                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29107                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29108                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29109                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29110                 /*
29111                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29112                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29113                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29114                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29115                 
29116                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29117                 r.barchart(330, 10, 300, 220, data1);
29118                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29119                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29120                 */
29121                 
29122                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29123                 // r.barchart(30, 30, 560, 250,  xdata, {
29124                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29125                 //     axis : "0 0 1 1",
29126                 //     axisxlabels :  xdata
29127                 //     //yvalues : cols,
29128                    
29129                 // });
29130 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29131 //        
29132 //        this.load(null,xdata,{
29133 //                axis : "0 0 1 1",
29134 //                axisxlabels :  xdata
29135 //                });
29136
29137     },
29138
29139     load : function(graphtype,xdata,opts)
29140     {
29141         this.raphael.clear();
29142         if(!graphtype) {
29143             graphtype = this.graphtype;
29144         }
29145         if(!opts){
29146             opts = this.opts;
29147         }
29148         var r = this.raphael,
29149             fin = function () {
29150                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29151             },
29152             fout = function () {
29153                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29154             },
29155             pfin = function() {
29156                 this.sector.stop();
29157                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29158
29159                 if (this.label) {
29160                     this.label[0].stop();
29161                     this.label[0].attr({ r: 7.5 });
29162                     this.label[1].attr({ "font-weight": 800 });
29163                 }
29164             },
29165             pfout = function() {
29166                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29167
29168                 if (this.label) {
29169                     this.label[0].animate({ r: 5 }, 500, "bounce");
29170                     this.label[1].attr({ "font-weight": 400 });
29171                 }
29172             };
29173
29174         switch(graphtype){
29175             case 'bar':
29176                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29177                 break;
29178             case 'hbar':
29179                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29180                 break;
29181             case 'pie':
29182 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29183 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29184 //            
29185                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29186                 
29187                 break;
29188
29189         }
29190         
29191         if(this.title){
29192             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29193         }
29194         
29195     },
29196     
29197     setTitle: function(o)
29198     {
29199         this.title = o;
29200     },
29201     
29202     initEvents: function() {
29203         
29204         if(!this.href){
29205             this.el.on('click', this.onClick, this);
29206         }
29207     },
29208     
29209     onClick : function(e)
29210     {
29211         Roo.log('img onclick');
29212         this.fireEvent('click', this, e);
29213     }
29214    
29215 });
29216
29217  
29218 /*
29219  * - LGPL
29220  *
29221  * numberBox
29222  * 
29223  */
29224 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29225
29226 /**
29227  * @class Roo.bootstrap.dash.NumberBox
29228  * @extends Roo.bootstrap.Component
29229  * Bootstrap NumberBox class
29230  * @cfg {String} headline Box headline
29231  * @cfg {String} content Box content
29232  * @cfg {String} icon Box icon
29233  * @cfg {String} footer Footer text
29234  * @cfg {String} fhref Footer href
29235  * 
29236  * @constructor
29237  * Create a new NumberBox
29238  * @param {Object} config The config object
29239  */
29240
29241
29242 Roo.bootstrap.dash.NumberBox = function(config){
29243     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29244     
29245 };
29246
29247 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29248     
29249     headline : '',
29250     content : '',
29251     icon : '',
29252     footer : '',
29253     fhref : '',
29254     ficon : '',
29255     
29256     getAutoCreate : function(){
29257         
29258         var cfg = {
29259             tag : 'div',
29260             cls : 'small-box ',
29261             cn : [
29262                 {
29263                     tag : 'div',
29264                     cls : 'inner',
29265                     cn :[
29266                         {
29267                             tag : 'h3',
29268                             cls : 'roo-headline',
29269                             html : this.headline
29270                         },
29271                         {
29272                             tag : 'p',
29273                             cls : 'roo-content',
29274                             html : this.content
29275                         }
29276                     ]
29277                 }
29278             ]
29279         };
29280         
29281         if(this.icon){
29282             cfg.cn.push({
29283                 tag : 'div',
29284                 cls : 'icon',
29285                 cn :[
29286                     {
29287                         tag : 'i',
29288                         cls : 'ion ' + this.icon
29289                     }
29290                 ]
29291             });
29292         }
29293         
29294         if(this.footer){
29295             var footer = {
29296                 tag : 'a',
29297                 cls : 'small-box-footer',
29298                 href : this.fhref || '#',
29299                 html : this.footer
29300             };
29301             
29302             cfg.cn.push(footer);
29303             
29304         }
29305         
29306         return  cfg;
29307     },
29308
29309     onRender : function(ct,position){
29310         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29311
29312
29313        
29314                 
29315     },
29316
29317     setHeadline: function (value)
29318     {
29319         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29320     },
29321     
29322     setFooter: function (value, href)
29323     {
29324         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29325         
29326         if(href){
29327             this.el.select('a.small-box-footer',true).first().attr('href', href);
29328         }
29329         
29330     },
29331
29332     setContent: function (value)
29333     {
29334         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29335     },
29336
29337     initEvents: function() 
29338     {   
29339         
29340     }
29341     
29342 });
29343
29344  
29345 /*
29346  * - LGPL
29347  *
29348  * TabBox
29349  * 
29350  */
29351 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29352
29353 /**
29354  * @class Roo.bootstrap.dash.TabBox
29355  * @extends Roo.bootstrap.Component
29356  * @children Roo.bootstrap.dash.TabPane
29357  * Bootstrap TabBox class
29358  * @cfg {String} title Title of the TabBox
29359  * @cfg {String} icon Icon of the TabBox
29360  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29361  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29362  * 
29363  * @constructor
29364  * Create a new TabBox
29365  * @param {Object} config The config object
29366  */
29367
29368
29369 Roo.bootstrap.dash.TabBox = function(config){
29370     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29371     this.addEvents({
29372         // raw events
29373         /**
29374          * @event addpane
29375          * When a pane is added
29376          * @param {Roo.bootstrap.dash.TabPane} pane
29377          */
29378         "addpane" : true,
29379         /**
29380          * @event activatepane
29381          * When a pane is activated
29382          * @param {Roo.bootstrap.dash.TabPane} pane
29383          */
29384         "activatepane" : true
29385         
29386          
29387     });
29388     
29389     this.panes = [];
29390 };
29391
29392 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29393
29394     title : '',
29395     icon : false,
29396     showtabs : true,
29397     tabScrollable : false,
29398     
29399     getChildContainer : function()
29400     {
29401         return this.el.select('.tab-content', true).first();
29402     },
29403     
29404     getAutoCreate : function(){
29405         
29406         var header = {
29407             tag: 'li',
29408             cls: 'pull-left header',
29409             html: this.title,
29410             cn : []
29411         };
29412         
29413         if(this.icon){
29414             header.cn.push({
29415                 tag: 'i',
29416                 cls: 'fa ' + this.icon
29417             });
29418         }
29419         
29420         var h = {
29421             tag: 'ul',
29422             cls: 'nav nav-tabs pull-right',
29423             cn: [
29424                 header
29425             ]
29426         };
29427         
29428         if(this.tabScrollable){
29429             h = {
29430                 tag: 'div',
29431                 cls: 'tab-header',
29432                 cn: [
29433                     {
29434                         tag: 'ul',
29435                         cls: 'nav nav-tabs pull-right',
29436                         cn: [
29437                             header
29438                         ]
29439                     }
29440                 ]
29441             };
29442         }
29443         
29444         var cfg = {
29445             tag: 'div',
29446             cls: 'nav-tabs-custom',
29447             cn: [
29448                 h,
29449                 {
29450                     tag: 'div',
29451                     cls: 'tab-content no-padding',
29452                     cn: []
29453                 }
29454             ]
29455         };
29456
29457         return  cfg;
29458     },
29459     initEvents : function()
29460     {
29461         //Roo.log('add add pane handler');
29462         this.on('addpane', this.onAddPane, this);
29463     },
29464      /**
29465      * Updates the box title
29466      * @param {String} html to set the title to.
29467      */
29468     setTitle : function(value)
29469     {
29470         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29471     },
29472     onAddPane : function(pane)
29473     {
29474         this.panes.push(pane);
29475         //Roo.log('addpane');
29476         //Roo.log(pane);
29477         // tabs are rendere left to right..
29478         if(!this.showtabs){
29479             return;
29480         }
29481         
29482         var ctr = this.el.select('.nav-tabs', true).first();
29483          
29484          
29485         var existing = ctr.select('.nav-tab',true);
29486         var qty = existing.getCount();;
29487         
29488         
29489         var tab = ctr.createChild({
29490             tag : 'li',
29491             cls : 'nav-tab' + (qty ? '' : ' active'),
29492             cn : [
29493                 {
29494                     tag : 'a',
29495                     href:'#',
29496                     html : pane.title
29497                 }
29498             ]
29499         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29500         pane.tab = tab;
29501         
29502         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29503         if (!qty) {
29504             pane.el.addClass('active');
29505         }
29506         
29507                 
29508     },
29509     onTabClick : function(ev,un,ob,pane)
29510     {
29511         //Roo.log('tab - prev default');
29512         ev.preventDefault();
29513         
29514         
29515         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29516         pane.tab.addClass('active');
29517         //Roo.log(pane.title);
29518         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29519         // technically we should have a deactivate event.. but maybe add later.
29520         // and it should not de-activate the selected tab...
29521         this.fireEvent('activatepane', pane);
29522         pane.el.addClass('active');
29523         pane.fireEvent('activate');
29524         
29525         
29526     },
29527     
29528     getActivePane : function()
29529     {
29530         var r = false;
29531         Roo.each(this.panes, function(p) {
29532             if(p.el.hasClass('active')){
29533                 r = p;
29534                 return false;
29535             }
29536             
29537             return;
29538         });
29539         
29540         return r;
29541     }
29542     
29543     
29544 });
29545
29546  
29547 /*
29548  * - LGPL
29549  *
29550  * Tab pane
29551  * 
29552  */
29553 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29554 /**
29555  * @class Roo.bootstrap.TabPane
29556  * @extends Roo.bootstrap.Component
29557  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29558  * Bootstrap TabPane class
29559  * @cfg {Boolean} active (false | true) Default false
29560  * @cfg {String} title title of panel
29561
29562  * 
29563  * @constructor
29564  * Create a new TabPane
29565  * @param {Object} config The config object
29566  */
29567
29568 Roo.bootstrap.dash.TabPane = function(config){
29569     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29570     
29571     this.addEvents({
29572         // raw events
29573         /**
29574          * @event activate
29575          * When a pane is activated
29576          * @param {Roo.bootstrap.dash.TabPane} pane
29577          */
29578         "activate" : true
29579          
29580     });
29581 };
29582
29583 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29584     
29585     active : false,
29586     title : '',
29587     
29588     // the tabBox that this is attached to.
29589     tab : false,
29590      
29591     getAutoCreate : function() 
29592     {
29593         var cfg = {
29594             tag: 'div',
29595             cls: 'tab-pane'
29596         };
29597         
29598         if(this.active){
29599             cfg.cls += ' active';
29600         }
29601         
29602         return cfg;
29603     },
29604     initEvents  : function()
29605     {
29606         //Roo.log('trigger add pane handler');
29607         this.parent().fireEvent('addpane', this)
29608     },
29609     
29610      /**
29611      * Updates the tab title 
29612      * @param {String} html to set the title to.
29613      */
29614     setTitle: function(str)
29615     {
29616         if (!this.tab) {
29617             return;
29618         }
29619         this.title = str;
29620         this.tab.select('a', true).first().dom.innerHTML = str;
29621         
29622     }
29623     
29624     
29625     
29626 });
29627
29628  
29629
29630
29631  /*
29632  * - LGPL
29633  *
29634  * Tooltip
29635  * 
29636  */
29637
29638 /**
29639  * @class Roo.bootstrap.Tooltip
29640  * Bootstrap Tooltip class
29641  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29642  * to determine which dom element triggers the tooltip.
29643  * 
29644  * It needs to add support for additional attributes like tooltip-position
29645  * 
29646  * @constructor
29647  * Create a new Toolti
29648  * @param {Object} config The config object
29649  */
29650
29651 Roo.bootstrap.Tooltip = function(config){
29652     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29653     
29654     this.alignment = Roo.bootstrap.Tooltip.alignment;
29655     
29656     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29657         this.alignment = config.alignment;
29658     }
29659     
29660 };
29661
29662 Roo.apply(Roo.bootstrap.Tooltip, {
29663     /**
29664      * @function init initialize tooltip monitoring.
29665      * @static
29666      */
29667     currentEl : false,
29668     currentTip : false,
29669     currentRegion : false,
29670     
29671     //  init : delay?
29672     
29673     init : function()
29674     {
29675         Roo.get(document).on('mouseover', this.enter ,this);
29676         Roo.get(document).on('mouseout', this.leave, this);
29677          
29678         
29679         this.currentTip = new Roo.bootstrap.Tooltip();
29680     },
29681     
29682     enter : function(ev)
29683     {
29684         var dom = ev.getTarget();
29685         
29686         //Roo.log(['enter',dom]);
29687         var el = Roo.fly(dom);
29688         if (this.currentEl) {
29689             //Roo.log(dom);
29690             //Roo.log(this.currentEl);
29691             //Roo.log(this.currentEl.contains(dom));
29692             if (this.currentEl == el) {
29693                 return;
29694             }
29695             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29696                 return;
29697             }
29698
29699         }
29700         
29701         if (this.currentTip.el) {
29702             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29703         }    
29704         //Roo.log(ev);
29705         
29706         if(!el || el.dom == document){
29707             return;
29708         }
29709         
29710         var bindEl = el; 
29711         var pel = false;
29712         if (!el.attr('tooltip')) {
29713             pel = el.findParent("[tooltip]");
29714             if (pel) {
29715                 bindEl = Roo.get(pel);
29716             }
29717         }
29718         
29719        
29720         
29721         // you can not look for children, as if el is the body.. then everythign is the child..
29722         if (!pel && !el.attr('tooltip')) { //
29723             if (!el.select("[tooltip]").elements.length) {
29724                 return;
29725             }
29726             // is the mouse over this child...?
29727             bindEl = el.select("[tooltip]").first();
29728             var xy = ev.getXY();
29729             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29730                 //Roo.log("not in region.");
29731                 return;
29732             }
29733             //Roo.log("child element over..");
29734             
29735         }
29736         this.currentEl = el;
29737         this.currentTip.bind(bindEl);
29738         this.currentRegion = Roo.lib.Region.getRegion(dom);
29739         this.currentTip.enter();
29740         
29741     },
29742     leave : function(ev)
29743     {
29744         var dom = ev.getTarget();
29745         //Roo.log(['leave',dom]);
29746         if (!this.currentEl) {
29747             return;
29748         }
29749         
29750         
29751         if (dom != this.currentEl.dom) {
29752             return;
29753         }
29754         var xy = ev.getXY();
29755         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29756             return;
29757         }
29758         // only activate leave if mouse cursor is outside... bounding box..
29759         
29760         
29761         
29762         
29763         if (this.currentTip) {
29764             this.currentTip.leave();
29765         }
29766         //Roo.log('clear currentEl');
29767         this.currentEl = false;
29768         
29769         
29770     },
29771     alignment : {
29772         'left' : ['r-l', [-2,0], 'right'],
29773         'right' : ['l-r', [2,0], 'left'],
29774         'bottom' : ['t-b', [0,2], 'top'],
29775         'top' : [ 'b-t', [0,-2], 'bottom']
29776     }
29777     
29778 });
29779
29780
29781 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29782     
29783     
29784     bindEl : false,
29785     
29786     delay : null, // can be { show : 300 , hide: 500}
29787     
29788     timeout : null,
29789     
29790     hoverState : null, //???
29791     
29792     placement : 'bottom', 
29793     
29794     alignment : false,
29795     
29796     getAutoCreate : function(){
29797     
29798         var cfg = {
29799            cls : 'tooltip',   
29800            role : 'tooltip',
29801            cn : [
29802                 {
29803                     cls : 'tooltip-arrow arrow'
29804                 },
29805                 {
29806                     cls : 'tooltip-inner'
29807                 }
29808            ]
29809         };
29810         
29811         return cfg;
29812     },
29813     bind : function(el)
29814     {
29815         this.bindEl = el;
29816     },
29817     
29818     initEvents : function()
29819     {
29820         this.arrowEl = this.el.select('.arrow', true).first();
29821         this.innerEl = this.el.select('.tooltip-inner', true).first();
29822     },
29823     
29824     enter : function () {
29825        
29826         if (this.timeout != null) {
29827             clearTimeout(this.timeout);
29828         }
29829         
29830         this.hoverState = 'in';
29831          //Roo.log("enter - show");
29832         if (!this.delay || !this.delay.show) {
29833             this.show();
29834             return;
29835         }
29836         var _t = this;
29837         this.timeout = setTimeout(function () {
29838             if (_t.hoverState == 'in') {
29839                 _t.show();
29840             }
29841         }, this.delay.show);
29842     },
29843     leave : function()
29844     {
29845         clearTimeout(this.timeout);
29846     
29847         this.hoverState = 'out';
29848          if (!this.delay || !this.delay.hide) {
29849             this.hide();
29850             return;
29851         }
29852        
29853         var _t = this;
29854         this.timeout = setTimeout(function () {
29855             //Roo.log("leave - timeout");
29856             
29857             if (_t.hoverState == 'out') {
29858                 _t.hide();
29859                 Roo.bootstrap.Tooltip.currentEl = false;
29860             }
29861         }, delay);
29862     },
29863     
29864     show : function (msg)
29865     {
29866         if (!this.el) {
29867             this.render(document.body);
29868         }
29869         // set content.
29870         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29871         
29872         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29873         
29874         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29875         
29876         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29877                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29878         
29879         var placement = typeof this.placement == 'function' ?
29880             this.placement.call(this, this.el, on_el) :
29881             this.placement;
29882             
29883         var autoToken = /\s?auto?\s?/i;
29884         var autoPlace = autoToken.test(placement);
29885         if (autoPlace) {
29886             placement = placement.replace(autoToken, '') || 'top';
29887         }
29888         
29889         //this.el.detach()
29890         //this.el.setXY([0,0]);
29891         this.el.show();
29892         //this.el.dom.style.display='block';
29893         
29894         //this.el.appendTo(on_el);
29895         
29896         var p = this.getPosition();
29897         var box = this.el.getBox();
29898         
29899         if (autoPlace) {
29900             // fixme..
29901         }
29902         
29903         var align = this.alignment[placement];
29904         
29905         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29906         
29907         if(placement == 'top' || placement == 'bottom'){
29908             if(xy[0] < 0){
29909                 placement = 'right';
29910             }
29911             
29912             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29913                 placement = 'left';
29914             }
29915             
29916             var scroll = Roo.select('body', true).first().getScroll();
29917             
29918             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29919                 placement = 'top';
29920             }
29921             
29922             align = this.alignment[placement];
29923             
29924             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29925             
29926         }
29927         
29928         var elems = document.getElementsByTagName('div');
29929         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29930         for (var i = 0; i < elems.length; i++) {
29931           var zindex = Number.parseInt(
29932                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29933                 10
29934           );
29935           if (zindex > highest) {
29936             highest = zindex;
29937           }
29938         }
29939         
29940         
29941         
29942         this.el.dom.style.zIndex = highest;
29943         
29944         this.el.alignTo(this.bindEl, align[0],align[1]);
29945         //var arrow = this.el.select('.arrow',true).first();
29946         //arrow.set(align[2], 
29947         
29948         this.el.addClass(placement);
29949         this.el.addClass("bs-tooltip-"+ placement);
29950         
29951         this.el.addClass('in fade show');
29952         
29953         this.hoverState = null;
29954         
29955         if (this.el.hasClass('fade')) {
29956             // fade it?
29957         }
29958         
29959         
29960         
29961         
29962         
29963     },
29964     hide : function()
29965     {
29966          
29967         if (!this.el) {
29968             return;
29969         }
29970         //this.el.setXY([0,0]);
29971         this.el.removeClass(['show', 'in']);
29972         //this.el.hide();
29973         
29974     }
29975     
29976 });
29977  
29978
29979  /*
29980  * - LGPL
29981  *
29982  * Location Picker
29983  * 
29984  */
29985
29986 /**
29987  * @class Roo.bootstrap.LocationPicker
29988  * @extends Roo.bootstrap.Component
29989  * Bootstrap LocationPicker class
29990  * @cfg {Number} latitude Position when init default 0
29991  * @cfg {Number} longitude Position when init default 0
29992  * @cfg {Number} zoom default 15
29993  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29994  * @cfg {Boolean} mapTypeControl default false
29995  * @cfg {Boolean} disableDoubleClickZoom default false
29996  * @cfg {Boolean} scrollwheel default true
29997  * @cfg {Boolean} streetViewControl default false
29998  * @cfg {Number} radius default 0
29999  * @cfg {String} locationName
30000  * @cfg {Boolean} draggable default true
30001  * @cfg {Boolean} enableAutocomplete default false
30002  * @cfg {Boolean} enableReverseGeocode default true
30003  * @cfg {String} markerTitle
30004  * 
30005  * @constructor
30006  * Create a new LocationPicker
30007  * @param {Object} config The config object
30008  */
30009
30010
30011 Roo.bootstrap.LocationPicker = function(config){
30012     
30013     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30014     
30015     this.addEvents({
30016         /**
30017          * @event initial
30018          * Fires when the picker initialized.
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          * @param {Google Location} location
30021          */
30022         initial : true,
30023         /**
30024          * @event positionchanged
30025          * Fires when the picker position changed.
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          * @param {Google Location} location
30028          */
30029         positionchanged : true,
30030         /**
30031          * @event resize
30032          * Fires when the map resize.
30033          * @param {Roo.bootstrap.LocationPicker} this
30034          */
30035         resize : true,
30036         /**
30037          * @event show
30038          * Fires when the map show.
30039          * @param {Roo.bootstrap.LocationPicker} this
30040          */
30041         show : true,
30042         /**
30043          * @event hide
30044          * Fires when the map hide.
30045          * @param {Roo.bootstrap.LocationPicker} this
30046          */
30047         hide : true,
30048         /**
30049          * @event mapClick
30050          * Fires when click the map.
30051          * @param {Roo.bootstrap.LocationPicker} this
30052          * @param {Map event} e
30053          */
30054         mapClick : true,
30055         /**
30056          * @event mapRightClick
30057          * Fires when right click the map.
30058          * @param {Roo.bootstrap.LocationPicker} this
30059          * @param {Map event} e
30060          */
30061         mapRightClick : true,
30062         /**
30063          * @event markerClick
30064          * Fires when click the marker.
30065          * @param {Roo.bootstrap.LocationPicker} this
30066          * @param {Map event} e
30067          */
30068         markerClick : true,
30069         /**
30070          * @event markerRightClick
30071          * Fires when right click the marker.
30072          * @param {Roo.bootstrap.LocationPicker} this
30073          * @param {Map event} e
30074          */
30075         markerRightClick : true,
30076         /**
30077          * @event OverlayViewDraw
30078          * Fires when OverlayView Draw
30079          * @param {Roo.bootstrap.LocationPicker} this
30080          */
30081         OverlayViewDraw : true,
30082         /**
30083          * @event OverlayViewOnAdd
30084          * Fires when OverlayView Draw
30085          * @param {Roo.bootstrap.LocationPicker} this
30086          */
30087         OverlayViewOnAdd : true,
30088         /**
30089          * @event OverlayViewOnRemove
30090          * Fires when OverlayView Draw
30091          * @param {Roo.bootstrap.LocationPicker} this
30092          */
30093         OverlayViewOnRemove : true,
30094         /**
30095          * @event OverlayViewShow
30096          * Fires when OverlayView Draw
30097          * @param {Roo.bootstrap.LocationPicker} this
30098          * @param {Pixel} cpx
30099          */
30100         OverlayViewShow : true,
30101         /**
30102          * @event OverlayViewHide
30103          * Fires when OverlayView Draw
30104          * @param {Roo.bootstrap.LocationPicker} this
30105          */
30106         OverlayViewHide : true,
30107         /**
30108          * @event loadexception
30109          * Fires when load google lib failed.
30110          * @param {Roo.bootstrap.LocationPicker} this
30111          */
30112         loadexception : true
30113     });
30114         
30115 };
30116
30117 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30118     
30119     gMapContext: false,
30120     
30121     latitude: 0,
30122     longitude: 0,
30123     zoom: 15,
30124     mapTypeId: false,
30125     mapTypeControl: false,
30126     disableDoubleClickZoom: false,
30127     scrollwheel: true,
30128     streetViewControl: false,
30129     radius: 0,
30130     locationName: '',
30131     draggable: true,
30132     enableAutocomplete: false,
30133     enableReverseGeocode: true,
30134     markerTitle: '',
30135     
30136     getAutoCreate: function()
30137     {
30138
30139         var cfg = {
30140             tag: 'div',
30141             cls: 'roo-location-picker'
30142         };
30143         
30144         return cfg
30145     },
30146     
30147     initEvents: function(ct, position)
30148     {       
30149         if(!this.el.getWidth() || this.isApplied()){
30150             return;
30151         }
30152         
30153         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30154         
30155         this.initial();
30156     },
30157     
30158     initial: function()
30159     {
30160         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30161             this.fireEvent('loadexception', this);
30162             return;
30163         }
30164         
30165         if(!this.mapTypeId){
30166             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30167         }
30168         
30169         this.gMapContext = this.GMapContext();
30170         
30171         this.initOverlayView();
30172         
30173         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30174         
30175         var _this = this;
30176                 
30177         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30178             _this.setPosition(_this.gMapContext.marker.position);
30179         });
30180         
30181         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30182             _this.fireEvent('mapClick', this, event);
30183             
30184         });
30185
30186         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30187             _this.fireEvent('mapRightClick', this, event);
30188             
30189         });
30190         
30191         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30192             _this.fireEvent('markerClick', this, event);
30193             
30194         });
30195
30196         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30197             _this.fireEvent('markerRightClick', this, event);
30198             
30199         });
30200         
30201         this.setPosition(this.gMapContext.location);
30202         
30203         this.fireEvent('initial', this, this.gMapContext.location);
30204     },
30205     
30206     initOverlayView: function()
30207     {
30208         var _this = this;
30209         
30210         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30211             
30212             draw: function()
30213             {
30214                 _this.fireEvent('OverlayViewDraw', _this);
30215             },
30216             
30217             onAdd: function()
30218             {
30219                 _this.fireEvent('OverlayViewOnAdd', _this);
30220             },
30221             
30222             onRemove: function()
30223             {
30224                 _this.fireEvent('OverlayViewOnRemove', _this);
30225             },
30226             
30227             show: function(cpx)
30228             {
30229                 _this.fireEvent('OverlayViewShow', _this, cpx);
30230             },
30231             
30232             hide: function()
30233             {
30234                 _this.fireEvent('OverlayViewHide', _this);
30235             }
30236             
30237         });
30238     },
30239     
30240     fromLatLngToContainerPixel: function(event)
30241     {
30242         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30243     },
30244     
30245     isApplied: function() 
30246     {
30247         return this.getGmapContext() == false ? false : true;
30248     },
30249     
30250     getGmapContext: function() 
30251     {
30252         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30253     },
30254     
30255     GMapContext: function() 
30256     {
30257         var position = new google.maps.LatLng(this.latitude, this.longitude);
30258         
30259         var _map = new google.maps.Map(this.el.dom, {
30260             center: position,
30261             zoom: this.zoom,
30262             mapTypeId: this.mapTypeId,
30263             mapTypeControl: this.mapTypeControl,
30264             disableDoubleClickZoom: this.disableDoubleClickZoom,
30265             scrollwheel: this.scrollwheel,
30266             streetViewControl: this.streetViewControl,
30267             locationName: this.locationName,
30268             draggable: this.draggable,
30269             enableAutocomplete: this.enableAutocomplete,
30270             enableReverseGeocode: this.enableReverseGeocode
30271         });
30272         
30273         var _marker = new google.maps.Marker({
30274             position: position,
30275             map: _map,
30276             title: this.markerTitle,
30277             draggable: this.draggable
30278         });
30279         
30280         return {
30281             map: _map,
30282             marker: _marker,
30283             circle: null,
30284             location: position,
30285             radius: this.radius,
30286             locationName: this.locationName,
30287             addressComponents: {
30288                 formatted_address: null,
30289                 addressLine1: null,
30290                 addressLine2: null,
30291                 streetName: null,
30292                 streetNumber: null,
30293                 city: null,
30294                 district: null,
30295                 state: null,
30296                 stateOrProvince: null
30297             },
30298             settings: this,
30299             domContainer: this.el.dom,
30300             geodecoder: new google.maps.Geocoder()
30301         };
30302     },
30303     
30304     drawCircle: function(center, radius, options) 
30305     {
30306         if (this.gMapContext.circle != null) {
30307             this.gMapContext.circle.setMap(null);
30308         }
30309         if (radius > 0) {
30310             radius *= 1;
30311             options = Roo.apply({}, options, {
30312                 strokeColor: "#0000FF",
30313                 strokeOpacity: .35,
30314                 strokeWeight: 2,
30315                 fillColor: "#0000FF",
30316                 fillOpacity: .2
30317             });
30318             
30319             options.map = this.gMapContext.map;
30320             options.radius = radius;
30321             options.center = center;
30322             this.gMapContext.circle = new google.maps.Circle(options);
30323             return this.gMapContext.circle;
30324         }
30325         
30326         return null;
30327     },
30328     
30329     setPosition: function(location) 
30330     {
30331         this.gMapContext.location = location;
30332         this.gMapContext.marker.setPosition(location);
30333         this.gMapContext.map.panTo(location);
30334         this.drawCircle(location, this.gMapContext.radius, {});
30335         
30336         var _this = this;
30337         
30338         if (this.gMapContext.settings.enableReverseGeocode) {
30339             this.gMapContext.geodecoder.geocode({
30340                 latLng: this.gMapContext.location
30341             }, function(results, status) {
30342                 
30343                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30344                     _this.gMapContext.locationName = results[0].formatted_address;
30345                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30346                     
30347                     _this.fireEvent('positionchanged', this, location);
30348                 }
30349             });
30350             
30351             return;
30352         }
30353         
30354         this.fireEvent('positionchanged', this, location);
30355     },
30356     
30357     resize: function()
30358     {
30359         google.maps.event.trigger(this.gMapContext.map, "resize");
30360         
30361         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30362         
30363         this.fireEvent('resize', this);
30364     },
30365     
30366     setPositionByLatLng: function(latitude, longitude)
30367     {
30368         this.setPosition(new google.maps.LatLng(latitude, longitude));
30369     },
30370     
30371     getCurrentPosition: function() 
30372     {
30373         return {
30374             latitude: this.gMapContext.location.lat(),
30375             longitude: this.gMapContext.location.lng()
30376         };
30377     },
30378     
30379     getAddressName: function() 
30380     {
30381         return this.gMapContext.locationName;
30382     },
30383     
30384     getAddressComponents: function() 
30385     {
30386         return this.gMapContext.addressComponents;
30387     },
30388     
30389     address_component_from_google_geocode: function(address_components) 
30390     {
30391         var result = {};
30392         
30393         for (var i = 0; i < address_components.length; i++) {
30394             var component = address_components[i];
30395             if (component.types.indexOf("postal_code") >= 0) {
30396                 result.postalCode = component.short_name;
30397             } else if (component.types.indexOf("street_number") >= 0) {
30398                 result.streetNumber = component.short_name;
30399             } else if (component.types.indexOf("route") >= 0) {
30400                 result.streetName = component.short_name;
30401             } else if (component.types.indexOf("neighborhood") >= 0) {
30402                 result.city = component.short_name;
30403             } else if (component.types.indexOf("locality") >= 0) {
30404                 result.city = component.short_name;
30405             } else if (component.types.indexOf("sublocality") >= 0) {
30406                 result.district = component.short_name;
30407             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30408                 result.stateOrProvince = component.short_name;
30409             } else if (component.types.indexOf("country") >= 0) {
30410                 result.country = component.short_name;
30411             }
30412         }
30413         
30414         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30415         result.addressLine2 = "";
30416         return result;
30417     },
30418     
30419     setZoomLevel: function(zoom)
30420     {
30421         this.gMapContext.map.setZoom(zoom);
30422     },
30423     
30424     show: function()
30425     {
30426         if(!this.el){
30427             return;
30428         }
30429         
30430         this.el.show();
30431         
30432         this.resize();
30433         
30434         this.fireEvent('show', this);
30435     },
30436     
30437     hide: function()
30438     {
30439         if(!this.el){
30440             return;
30441         }
30442         
30443         this.el.hide();
30444         
30445         this.fireEvent('hide', this);
30446     }
30447     
30448 });
30449
30450 Roo.apply(Roo.bootstrap.LocationPicker, {
30451     
30452     OverlayView : function(map, options)
30453     {
30454         options = options || {};
30455         
30456         this.setMap(map);
30457     }
30458     
30459     
30460 });/**
30461  * @class Roo.bootstrap.Alert
30462  * @extends Roo.bootstrap.Component
30463  * Bootstrap Alert class - shows an alert area box
30464  * eg
30465  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30466   Enter a valid email address
30467 </div>
30468  * @licence LGPL
30469  * @cfg {String} title The title of alert
30470  * @cfg {String} html The content of alert
30471  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30472  * @cfg {String} fa font-awesomeicon
30473  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30474  * @cfg {Boolean} close true to show a x closer
30475  * 
30476  * 
30477  * @constructor
30478  * Create a new alert
30479  * @param {Object} config The config object
30480  */
30481
30482
30483 Roo.bootstrap.Alert = function(config){
30484     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30485     
30486 };
30487
30488 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30489     
30490     title: '',
30491     html: '',
30492     weight: false,
30493     fa: false,
30494     faicon: false, // BC
30495     close : false,
30496     
30497     
30498     getAutoCreate : function()
30499     {
30500         
30501         var cfg = {
30502             tag : 'div',
30503             cls : 'alert',
30504             cn : [
30505                 {
30506                     tag: 'button',
30507                     type :  "button",
30508                     cls: "close",
30509                     html : '×',
30510                     style : this.close ? '' : 'display:none'
30511                 },
30512                 {
30513                     tag : 'i',
30514                     cls : 'roo-alert-icon'
30515                     
30516                 },
30517                 {
30518                     tag : 'b',
30519                     cls : 'roo-alert-title',
30520                     html : this.title
30521                 },
30522                 {
30523                     tag : 'span',
30524                     cls : 'roo-alert-text',
30525                     html : this.html
30526                 }
30527             ]
30528         };
30529         
30530         if(this.faicon){
30531             cfg.cn[0].cls += ' fa ' + this.faicon;
30532         }
30533         if(this.fa){
30534             cfg.cn[0].cls += ' fa ' + this.fa;
30535         }
30536         
30537         if(this.weight){
30538             cfg.cls += ' alert-' + this.weight;
30539         }
30540         
30541         return cfg;
30542     },
30543     
30544     initEvents: function() 
30545     {
30546         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30547         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30548         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30549         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30550         if (this.seconds > 0) {
30551             this.hide.defer(this.seconds, this);
30552         }
30553     },
30554     /**
30555      * Set the Title Message HTML
30556      * @param {String} html
30557      */
30558     setTitle : function(str)
30559     {
30560         this.titleEl.dom.innerHTML = str;
30561     },
30562      
30563      /**
30564      * Set the Body Message HTML
30565      * @param {String} html
30566      */
30567     setHtml : function(str)
30568     {
30569         this.htmlEl.dom.innerHTML = str;
30570     },
30571     /**
30572      * Set the Weight of the alert
30573      * @param {String} (success|info|warning|danger) weight
30574      */
30575     
30576     setWeight : function(weight)
30577     {
30578         if(this.weight){
30579             this.el.removeClass('alert-' + this.weight);
30580         }
30581         
30582         this.weight = weight;
30583         
30584         this.el.addClass('alert-' + this.weight);
30585     },
30586       /**
30587      * Set the Icon of the alert
30588      * @param {String} see fontawsome names (name without the 'fa-' bit)
30589      */
30590     setIcon : function(icon)
30591     {
30592         if(this.faicon){
30593             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30594         }
30595         
30596         this.faicon = icon;
30597         
30598         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30599     },
30600     /**
30601      * Hide the Alert
30602      */
30603     hide: function() 
30604     {
30605         this.el.hide();   
30606     },
30607     /**
30608      * Show the Alert
30609      */
30610     show: function() 
30611     {  
30612         this.el.show();   
30613     }
30614     
30615 });
30616
30617  
30618 /*
30619 * Licence: LGPL
30620 */
30621
30622 /**
30623  * @class Roo.bootstrap.UploadCropbox
30624  * @extends Roo.bootstrap.Component
30625  * Bootstrap UploadCropbox class
30626  * @cfg {String} emptyText show when image has been loaded
30627  * @cfg {String} rotateNotify show when image too small to rotate
30628  * @cfg {Number} errorTimeout default 3000
30629  * @cfg {Number} minWidth default 300
30630  * @cfg {Number} minHeight default 300
30631  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30632  * @cfg {Boolean} isDocument (true|false) default false
30633  * @cfg {String} url action url
30634  * @cfg {String} paramName default 'imageUpload'
30635  * @cfg {String} method default POST
30636  * @cfg {Boolean} loadMask (true|false) default true
30637  * @cfg {Boolean} loadingText default 'Loading...'
30638  * 
30639  * @constructor
30640  * Create a new UploadCropbox
30641  * @param {Object} config The config object
30642  */
30643
30644 Roo.bootstrap.UploadCropbox = function(config){
30645     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30646     
30647     this.addEvents({
30648         /**
30649          * @event beforeselectfile
30650          * Fire before select file
30651          * @param {Roo.bootstrap.UploadCropbox} this
30652          */
30653         "beforeselectfile" : true,
30654         /**
30655          * @event initial
30656          * Fire after initEvent
30657          * @param {Roo.bootstrap.UploadCropbox} this
30658          */
30659         "initial" : true,
30660         /**
30661          * @event crop
30662          * Fire after initEvent
30663          * @param {Roo.bootstrap.UploadCropbox} this
30664          * @param {String} data
30665          */
30666         "crop" : true,
30667         /**
30668          * @event prepare
30669          * Fire when preparing the file data
30670          * @param {Roo.bootstrap.UploadCropbox} this
30671          * @param {Object} file
30672          */
30673         "prepare" : true,
30674         /**
30675          * @event exception
30676          * Fire when get exception
30677          * @param {Roo.bootstrap.UploadCropbox} this
30678          * @param {XMLHttpRequest} xhr
30679          */
30680         "exception" : true,
30681         /**
30682          * @event beforeloadcanvas
30683          * Fire before load the canvas
30684          * @param {Roo.bootstrap.UploadCropbox} this
30685          * @param {String} src
30686          */
30687         "beforeloadcanvas" : true,
30688         /**
30689          * @event trash
30690          * Fire when trash image
30691          * @param {Roo.bootstrap.UploadCropbox} this
30692          */
30693         "trash" : true,
30694         /**
30695          * @event download
30696          * Fire when download the image
30697          * @param {Roo.bootstrap.UploadCropbox} this
30698          */
30699         "download" : true,
30700         /**
30701          * @event footerbuttonclick
30702          * Fire when footerbuttonclick
30703          * @param {Roo.bootstrap.UploadCropbox} this
30704          * @param {String} type
30705          */
30706         "footerbuttonclick" : true,
30707         /**
30708          * @event resize
30709          * Fire when resize
30710          * @param {Roo.bootstrap.UploadCropbox} this
30711          */
30712         "resize" : true,
30713         /**
30714          * @event rotate
30715          * Fire when rotate the image
30716          * @param {Roo.bootstrap.UploadCropbox} this
30717          * @param {String} pos
30718          */
30719         "rotate" : true,
30720         /**
30721          * @event inspect
30722          * Fire when inspect the file
30723          * @param {Roo.bootstrap.UploadCropbox} this
30724          * @param {Object} file
30725          */
30726         "inspect" : true,
30727         /**
30728          * @event upload
30729          * Fire when xhr upload the file
30730          * @param {Roo.bootstrap.UploadCropbox} this
30731          * @param {Object} data
30732          */
30733         "upload" : true,
30734         /**
30735          * @event arrange
30736          * Fire when arrange the file data
30737          * @param {Roo.bootstrap.UploadCropbox} this
30738          * @param {Object} formData
30739          */
30740         "arrange" : true
30741     });
30742     
30743     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30744 };
30745
30746 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30747     
30748     emptyText : 'Click to upload image',
30749     rotateNotify : 'Image is too small to rotate',
30750     errorTimeout : 3000,
30751     scale : 0,
30752     baseScale : 1,
30753     rotate : 0,
30754     dragable : false,
30755     pinching : false,
30756     mouseX : 0,
30757     mouseY : 0,
30758     cropData : false,
30759     minWidth : 300,
30760     minHeight : 300,
30761     file : false,
30762     exif : {},
30763     baseRotate : 1,
30764     cropType : 'image/jpeg',
30765     buttons : false,
30766     canvasLoaded : false,
30767     isDocument : false,
30768     method : 'POST',
30769     paramName : 'imageUpload',
30770     loadMask : true,
30771     loadingText : 'Loading...',
30772     maskEl : false,
30773     
30774     getAutoCreate : function()
30775     {
30776         var cfg = {
30777             tag : 'div',
30778             cls : 'roo-upload-cropbox',
30779             cn : [
30780                 {
30781                     tag : 'input',
30782                     cls : 'roo-upload-cropbox-selector',
30783                     type : 'file'
30784                 },
30785                 {
30786                     tag : 'div',
30787                     cls : 'roo-upload-cropbox-body',
30788                     style : 'cursor:pointer',
30789                     cn : [
30790                         {
30791                             tag : 'div',
30792                             cls : 'roo-upload-cropbox-preview'
30793                         },
30794                         {
30795                             tag : 'div',
30796                             cls : 'roo-upload-cropbox-thumb'
30797                         },
30798                         {
30799                             tag : 'div',
30800                             cls : 'roo-upload-cropbox-empty-notify',
30801                             html : this.emptyText
30802                         },
30803                         {
30804                             tag : 'div',
30805                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30806                             html : this.rotateNotify
30807                         }
30808                     ]
30809                 },
30810                 {
30811                     tag : 'div',
30812                     cls : 'roo-upload-cropbox-footer',
30813                     cn : {
30814                         tag : 'div',
30815                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30816                         cn : []
30817                     }
30818                 }
30819             ]
30820         };
30821         
30822         return cfg;
30823     },
30824     
30825     onRender : function(ct, position)
30826     {
30827         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30828         
30829         if (this.buttons.length) {
30830             
30831             Roo.each(this.buttons, function(bb) {
30832                 
30833                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30834                 
30835                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30836                 
30837             }, this);
30838         }
30839         
30840         if(this.loadMask){
30841             this.maskEl = this.el;
30842         }
30843     },
30844     
30845     initEvents : function()
30846     {
30847         this.urlAPI = (window.createObjectURL && window) || 
30848                                 (window.URL && URL.revokeObjectURL && URL) || 
30849                                 (window.webkitURL && webkitURL);
30850                         
30851         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30852         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30853         
30854         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30855         this.selectorEl.hide();
30856         
30857         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30858         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30859         
30860         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30861         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30862         this.thumbEl.hide();
30863         
30864         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30865         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30866         
30867         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30868         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30869         this.errorEl.hide();
30870         
30871         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30872         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30873         this.footerEl.hide();
30874         
30875         this.setThumbBoxSize();
30876         
30877         this.bind();
30878         
30879         this.resize();
30880         
30881         this.fireEvent('initial', this);
30882     },
30883
30884     bind : function()
30885     {
30886         var _this = this;
30887         
30888         window.addEventListener("resize", function() { _this.resize(); } );
30889         
30890         this.bodyEl.on('click', this.beforeSelectFile, this);
30891         
30892         if(Roo.isTouch){
30893             this.bodyEl.on('touchstart', this.onTouchStart, this);
30894             this.bodyEl.on('touchmove', this.onTouchMove, this);
30895             this.bodyEl.on('touchend', this.onTouchEnd, this);
30896         }
30897         
30898         if(!Roo.isTouch){
30899             this.bodyEl.on('mousedown', this.onMouseDown, this);
30900             this.bodyEl.on('mousemove', this.onMouseMove, this);
30901             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30902             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30903             Roo.get(document).on('mouseup', this.onMouseUp, this);
30904         }
30905         
30906         this.selectorEl.on('change', this.onFileSelected, this);
30907     },
30908     
30909     reset : function()
30910     {    
30911         this.scale = 0;
30912         this.baseScale = 1;
30913         this.rotate = 0;
30914         this.baseRotate = 1;
30915         this.dragable = false;
30916         this.pinching = false;
30917         this.mouseX = 0;
30918         this.mouseY = 0;
30919         this.cropData = false;
30920         this.notifyEl.dom.innerHTML = this.emptyText;
30921         
30922         this.selectorEl.dom.value = '';
30923         
30924     },
30925     
30926     resize : function()
30927     {
30928         if(this.fireEvent('resize', this) != false){
30929             this.setThumbBoxPosition();
30930             this.setCanvasPosition();
30931         }
30932     },
30933     
30934     onFooterButtonClick : function(e, el, o, type)
30935     {
30936         switch (type) {
30937             case 'rotate-left' :
30938                 this.onRotateLeft(e);
30939                 break;
30940             case 'rotate-right' :
30941                 this.onRotateRight(e);
30942                 break;
30943             case 'picture' :
30944                 this.beforeSelectFile(e);
30945                 break;
30946             case 'trash' :
30947                 this.trash(e);
30948                 break;
30949             case 'crop' :
30950                 this.crop(e);
30951                 break;
30952             case 'download' :
30953                 this.download(e);
30954                 break;
30955             default :
30956                 break;
30957         }
30958         
30959         this.fireEvent('footerbuttonclick', this, type);
30960     },
30961     
30962     beforeSelectFile : function(e)
30963     {
30964         e.preventDefault();
30965         
30966         if(this.fireEvent('beforeselectfile', this) != false){
30967             this.selectorEl.dom.click();
30968         }
30969     },
30970     
30971     onFileSelected : function(e)
30972     {
30973         e.preventDefault();
30974         
30975         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30976             return;
30977         }
30978         
30979         var file = this.selectorEl.dom.files[0];
30980         
30981         if(this.fireEvent('inspect', this, file) != false){
30982             this.prepare(file);
30983         }
30984         
30985     },
30986     
30987     trash : function(e)
30988     {
30989         this.fireEvent('trash', this);
30990     },
30991     
30992     download : function(e)
30993     {
30994         this.fireEvent('download', this);
30995     },
30996     
30997     loadCanvas : function(src)
30998     {   
30999         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31000             
31001             this.reset();
31002             
31003             this.imageEl = document.createElement('img');
31004             
31005             var _this = this;
31006             
31007             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31008             
31009             this.imageEl.src = src;
31010         }
31011     },
31012     
31013     onLoadCanvas : function()
31014     {   
31015         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31016         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31017         
31018         this.bodyEl.un('click', this.beforeSelectFile, this);
31019         
31020         this.notifyEl.hide();
31021         this.thumbEl.show();
31022         this.footerEl.show();
31023         
31024         this.baseRotateLevel();
31025         
31026         if(this.isDocument){
31027             this.setThumbBoxSize();
31028         }
31029         
31030         this.setThumbBoxPosition();
31031         
31032         this.baseScaleLevel();
31033         
31034         this.draw();
31035         
31036         this.resize();
31037         
31038         this.canvasLoaded = true;
31039         
31040         if(this.loadMask){
31041             this.maskEl.unmask();
31042         }
31043         
31044     },
31045     
31046     setCanvasPosition : function()
31047     {   
31048         if(!this.canvasEl){
31049             return;
31050         }
31051         
31052         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31053         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31054         
31055         this.previewEl.setLeft(pw);
31056         this.previewEl.setTop(ph);
31057         
31058     },
31059     
31060     onMouseDown : function(e)
31061     {   
31062         e.stopEvent();
31063         
31064         this.dragable = true;
31065         this.pinching = false;
31066         
31067         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31068             this.dragable = false;
31069             return;
31070         }
31071         
31072         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31073         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31074         
31075     },
31076     
31077     onMouseMove : function(e)
31078     {   
31079         e.stopEvent();
31080         
31081         if(!this.canvasLoaded){
31082             return;
31083         }
31084         
31085         if (!this.dragable){
31086             return;
31087         }
31088         
31089         var minX = Math.ceil(this.thumbEl.getLeft(true));
31090         var minY = Math.ceil(this.thumbEl.getTop(true));
31091         
31092         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31093         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31094         
31095         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31096         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31097         
31098         x = x - this.mouseX;
31099         y = y - this.mouseY;
31100         
31101         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31102         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31103         
31104         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31105         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31106         
31107         this.previewEl.setLeft(bgX);
31108         this.previewEl.setTop(bgY);
31109         
31110         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31111         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31112     },
31113     
31114     onMouseUp : function(e)
31115     {   
31116         e.stopEvent();
31117         
31118         this.dragable = false;
31119     },
31120     
31121     onMouseWheel : function(e)
31122     {   
31123         e.stopEvent();
31124         
31125         this.startScale = this.scale;
31126         
31127         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31128         
31129         if(!this.zoomable()){
31130             this.scale = this.startScale;
31131             return;
31132         }
31133         
31134         this.draw();
31135         
31136         return;
31137     },
31138     
31139     zoomable : function()
31140     {
31141         var minScale = this.thumbEl.getWidth() / this.minWidth;
31142         
31143         if(this.minWidth < this.minHeight){
31144             minScale = this.thumbEl.getHeight() / this.minHeight;
31145         }
31146         
31147         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31148         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31149         
31150         if(
31151                 this.isDocument &&
31152                 (this.rotate == 0 || this.rotate == 180) && 
31153                 (
31154                     width > this.imageEl.OriginWidth || 
31155                     height > this.imageEl.OriginHeight ||
31156                     (width < this.minWidth && height < this.minHeight)
31157                 )
31158         ){
31159             return false;
31160         }
31161         
31162         if(
31163                 this.isDocument &&
31164                 (this.rotate == 90 || this.rotate == 270) && 
31165                 (
31166                     width > this.imageEl.OriginWidth || 
31167                     height > this.imageEl.OriginHeight ||
31168                     (width < this.minHeight && height < this.minWidth)
31169                 )
31170         ){
31171             return false;
31172         }
31173         
31174         if(
31175                 !this.isDocument &&
31176                 (this.rotate == 0 || this.rotate == 180) && 
31177                 (
31178                     width < this.minWidth || 
31179                     width > this.imageEl.OriginWidth || 
31180                     height < this.minHeight || 
31181                     height > this.imageEl.OriginHeight
31182                 )
31183         ){
31184             return false;
31185         }
31186         
31187         if(
31188                 !this.isDocument &&
31189                 (this.rotate == 90 || this.rotate == 270) && 
31190                 (
31191                     width < this.minHeight || 
31192                     width > this.imageEl.OriginWidth || 
31193                     height < this.minWidth || 
31194                     height > this.imageEl.OriginHeight
31195                 )
31196         ){
31197             return false;
31198         }
31199         
31200         return true;
31201         
31202     },
31203     
31204     onRotateLeft : function(e)
31205     {   
31206         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31207             
31208             var minScale = this.thumbEl.getWidth() / this.minWidth;
31209             
31210             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31211             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31212             
31213             this.startScale = this.scale;
31214             
31215             while (this.getScaleLevel() < minScale){
31216             
31217                 this.scale = this.scale + 1;
31218                 
31219                 if(!this.zoomable()){
31220                     break;
31221                 }
31222                 
31223                 if(
31224                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31225                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31226                 ){
31227                     continue;
31228                 }
31229                 
31230                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31231
31232                 this.draw();
31233                 
31234                 return;
31235             }
31236             
31237             this.scale = this.startScale;
31238             
31239             this.onRotateFail();
31240             
31241             return false;
31242         }
31243         
31244         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31245
31246         if(this.isDocument){
31247             this.setThumbBoxSize();
31248             this.setThumbBoxPosition();
31249             this.setCanvasPosition();
31250         }
31251         
31252         this.draw();
31253         
31254         this.fireEvent('rotate', this, 'left');
31255         
31256     },
31257     
31258     onRotateRight : function(e)
31259     {
31260         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31261             
31262             var minScale = this.thumbEl.getWidth() / this.minWidth;
31263         
31264             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31265             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31266             
31267             this.startScale = this.scale;
31268             
31269             while (this.getScaleLevel() < minScale){
31270             
31271                 this.scale = this.scale + 1;
31272                 
31273                 if(!this.zoomable()){
31274                     break;
31275                 }
31276                 
31277                 if(
31278                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31279                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31280                 ){
31281                     continue;
31282                 }
31283                 
31284                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31285
31286                 this.draw();
31287                 
31288                 return;
31289             }
31290             
31291             this.scale = this.startScale;
31292             
31293             this.onRotateFail();
31294             
31295             return false;
31296         }
31297         
31298         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31299
31300         if(this.isDocument){
31301             this.setThumbBoxSize();
31302             this.setThumbBoxPosition();
31303             this.setCanvasPosition();
31304         }
31305         
31306         this.draw();
31307         
31308         this.fireEvent('rotate', this, 'right');
31309     },
31310     
31311     onRotateFail : function()
31312     {
31313         this.errorEl.show(true);
31314         
31315         var _this = this;
31316         
31317         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31318     },
31319     
31320     draw : function()
31321     {
31322         this.previewEl.dom.innerHTML = '';
31323         
31324         var canvasEl = document.createElement("canvas");
31325         
31326         var contextEl = canvasEl.getContext("2d");
31327         
31328         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31329         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31330         var center = this.imageEl.OriginWidth / 2;
31331         
31332         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31333             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31334             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31335             center = this.imageEl.OriginHeight / 2;
31336         }
31337         
31338         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31339         
31340         contextEl.translate(center, center);
31341         contextEl.rotate(this.rotate * Math.PI / 180);
31342
31343         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31344         
31345         this.canvasEl = document.createElement("canvas");
31346         
31347         this.contextEl = this.canvasEl.getContext("2d");
31348         
31349         switch (this.rotate) {
31350             case 0 :
31351                 
31352                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31353                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31354                 
31355                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31356                 
31357                 break;
31358             case 90 : 
31359                 
31360                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31361                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31362                 
31363                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31364                     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);
31365                     break;
31366                 }
31367                 
31368                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31369                 
31370                 break;
31371             case 180 :
31372                 
31373                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31374                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31375                 
31376                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31377                     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);
31378                     break;
31379                 }
31380                 
31381                 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);
31382                 
31383                 break;
31384             case 270 :
31385                 
31386                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31387                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31388         
31389                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31390                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31391                     break;
31392                 }
31393                 
31394                 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);
31395                 
31396                 break;
31397             default : 
31398                 break;
31399         }
31400         
31401         this.previewEl.appendChild(this.canvasEl);
31402         
31403         this.setCanvasPosition();
31404     },
31405     
31406     crop : function()
31407     {
31408         if(!this.canvasLoaded){
31409             return;
31410         }
31411         
31412         var imageCanvas = document.createElement("canvas");
31413         
31414         var imageContext = imageCanvas.getContext("2d");
31415         
31416         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31417         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31418         
31419         var center = imageCanvas.width / 2;
31420         
31421         imageContext.translate(center, center);
31422         
31423         imageContext.rotate(this.rotate * Math.PI / 180);
31424         
31425         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31426         
31427         var canvas = document.createElement("canvas");
31428         
31429         var context = canvas.getContext("2d");
31430                 
31431         canvas.width = this.minWidth;
31432         canvas.height = this.minHeight;
31433
31434         switch (this.rotate) {
31435             case 0 :
31436                 
31437                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31438                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31439                 
31440                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31441                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31442                 
31443                 var targetWidth = this.minWidth - 2 * x;
31444                 var targetHeight = this.minHeight - 2 * y;
31445                 
31446                 var scale = 1;
31447                 
31448                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31449                     scale = targetWidth / width;
31450                 }
31451                 
31452                 if(x > 0 && y == 0){
31453                     scale = targetHeight / height;
31454                 }
31455                 
31456                 if(x > 0 && y > 0){
31457                     scale = targetWidth / width;
31458                     
31459                     if(width < height){
31460                         scale = targetHeight / height;
31461                     }
31462                 }
31463                 
31464                 context.scale(scale, scale);
31465                 
31466                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31467                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31468
31469                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31470                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31471
31472                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31473                 
31474                 break;
31475             case 90 : 
31476                 
31477                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31478                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31479                 
31480                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31481                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31482                 
31483                 var targetWidth = this.minWidth - 2 * x;
31484                 var targetHeight = this.minHeight - 2 * y;
31485                 
31486                 var scale = 1;
31487                 
31488                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31489                     scale = targetWidth / width;
31490                 }
31491                 
31492                 if(x > 0 && y == 0){
31493                     scale = targetHeight / height;
31494                 }
31495                 
31496                 if(x > 0 && y > 0){
31497                     scale = targetWidth / width;
31498                     
31499                     if(width < height){
31500                         scale = targetHeight / height;
31501                     }
31502                 }
31503                 
31504                 context.scale(scale, scale);
31505                 
31506                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31507                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31508
31509                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31510                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31511                 
31512                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31513                 
31514                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31515                 
31516                 break;
31517             case 180 :
31518                 
31519                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31520                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31521                 
31522                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31523                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31524                 
31525                 var targetWidth = this.minWidth - 2 * x;
31526                 var targetHeight = this.minHeight - 2 * y;
31527                 
31528                 var scale = 1;
31529                 
31530                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31531                     scale = targetWidth / width;
31532                 }
31533                 
31534                 if(x > 0 && y == 0){
31535                     scale = targetHeight / height;
31536                 }
31537                 
31538                 if(x > 0 && y > 0){
31539                     scale = targetWidth / width;
31540                     
31541                     if(width < height){
31542                         scale = targetHeight / height;
31543                     }
31544                 }
31545                 
31546                 context.scale(scale, scale);
31547                 
31548                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31549                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31550
31551                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31552                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31553
31554                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31555                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31556                 
31557                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31558                 
31559                 break;
31560             case 270 :
31561                 
31562                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31563                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31564                 
31565                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31566                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31567                 
31568                 var targetWidth = this.minWidth - 2 * x;
31569                 var targetHeight = this.minHeight - 2 * y;
31570                 
31571                 var scale = 1;
31572                 
31573                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31574                     scale = targetWidth / width;
31575                 }
31576                 
31577                 if(x > 0 && y == 0){
31578                     scale = targetHeight / height;
31579                 }
31580                 
31581                 if(x > 0 && y > 0){
31582                     scale = targetWidth / width;
31583                     
31584                     if(width < height){
31585                         scale = targetHeight / height;
31586                     }
31587                 }
31588                 
31589                 context.scale(scale, scale);
31590                 
31591                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31592                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31593
31594                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31595                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31596                 
31597                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31598                 
31599                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31600                 
31601                 break;
31602             default : 
31603                 break;
31604         }
31605         
31606         this.cropData = canvas.toDataURL(this.cropType);
31607         
31608         if(this.fireEvent('crop', this, this.cropData) !== false){
31609             this.process(this.file, this.cropData);
31610         }
31611         
31612         return;
31613         
31614     },
31615     
31616     setThumbBoxSize : function()
31617     {
31618         var width, height;
31619         
31620         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31621             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31622             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31623             
31624             this.minWidth = width;
31625             this.minHeight = height;
31626             
31627             if(this.rotate == 90 || this.rotate == 270){
31628                 this.minWidth = height;
31629                 this.minHeight = width;
31630             }
31631         }
31632         
31633         height = 300;
31634         width = Math.ceil(this.minWidth * height / this.minHeight);
31635         
31636         if(this.minWidth > this.minHeight){
31637             width = 300;
31638             height = Math.ceil(this.minHeight * width / this.minWidth);
31639         }
31640         
31641         this.thumbEl.setStyle({
31642             width : width + 'px',
31643             height : height + 'px'
31644         });
31645
31646         return;
31647             
31648     },
31649     
31650     setThumbBoxPosition : function()
31651     {
31652         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31653         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31654         
31655         this.thumbEl.setLeft(x);
31656         this.thumbEl.setTop(y);
31657         
31658     },
31659     
31660     baseRotateLevel : function()
31661     {
31662         this.baseRotate = 1;
31663         
31664         if(
31665                 typeof(this.exif) != 'undefined' &&
31666                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31667                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31668         ){
31669             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31670         }
31671         
31672         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31673         
31674     },
31675     
31676     baseScaleLevel : function()
31677     {
31678         var width, height;
31679         
31680         if(this.isDocument){
31681             
31682             if(this.baseRotate == 6 || this.baseRotate == 8){
31683             
31684                 height = this.thumbEl.getHeight();
31685                 this.baseScale = height / this.imageEl.OriginWidth;
31686
31687                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31688                     width = this.thumbEl.getWidth();
31689                     this.baseScale = width / this.imageEl.OriginHeight;
31690                 }
31691
31692                 return;
31693             }
31694
31695             height = this.thumbEl.getHeight();
31696             this.baseScale = height / this.imageEl.OriginHeight;
31697
31698             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31699                 width = this.thumbEl.getWidth();
31700                 this.baseScale = width / this.imageEl.OriginWidth;
31701             }
31702
31703             return;
31704         }
31705         
31706         if(this.baseRotate == 6 || this.baseRotate == 8){
31707             
31708             width = this.thumbEl.getHeight();
31709             this.baseScale = width / this.imageEl.OriginHeight;
31710             
31711             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31712                 height = this.thumbEl.getWidth();
31713                 this.baseScale = height / this.imageEl.OriginHeight;
31714             }
31715             
31716             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31717                 height = this.thumbEl.getWidth();
31718                 this.baseScale = height / this.imageEl.OriginHeight;
31719                 
31720                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31721                     width = this.thumbEl.getHeight();
31722                     this.baseScale = width / this.imageEl.OriginWidth;
31723                 }
31724             }
31725             
31726             return;
31727         }
31728         
31729         width = this.thumbEl.getWidth();
31730         this.baseScale = width / this.imageEl.OriginWidth;
31731         
31732         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31733             height = this.thumbEl.getHeight();
31734             this.baseScale = height / this.imageEl.OriginHeight;
31735         }
31736         
31737         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31738             
31739             height = this.thumbEl.getHeight();
31740             this.baseScale = height / this.imageEl.OriginHeight;
31741             
31742             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31743                 width = this.thumbEl.getWidth();
31744                 this.baseScale = width / this.imageEl.OriginWidth;
31745             }
31746             
31747         }
31748         
31749         return;
31750     },
31751     
31752     getScaleLevel : function()
31753     {
31754         return this.baseScale * Math.pow(1.1, this.scale);
31755     },
31756     
31757     onTouchStart : function(e)
31758     {
31759         if(!this.canvasLoaded){
31760             this.beforeSelectFile(e);
31761             return;
31762         }
31763         
31764         var touches = e.browserEvent.touches;
31765         
31766         if(!touches){
31767             return;
31768         }
31769         
31770         if(touches.length == 1){
31771             this.onMouseDown(e);
31772             return;
31773         }
31774         
31775         if(touches.length != 2){
31776             return;
31777         }
31778         
31779         var coords = [];
31780         
31781         for(var i = 0, finger; finger = touches[i]; i++){
31782             coords.push(finger.pageX, finger.pageY);
31783         }
31784         
31785         var x = Math.pow(coords[0] - coords[2], 2);
31786         var y = Math.pow(coords[1] - coords[3], 2);
31787         
31788         this.startDistance = Math.sqrt(x + y);
31789         
31790         this.startScale = this.scale;
31791         
31792         this.pinching = true;
31793         this.dragable = false;
31794         
31795     },
31796     
31797     onTouchMove : function(e)
31798     {
31799         if(!this.pinching && !this.dragable){
31800             return;
31801         }
31802         
31803         var touches = e.browserEvent.touches;
31804         
31805         if(!touches){
31806             return;
31807         }
31808         
31809         if(this.dragable){
31810             this.onMouseMove(e);
31811             return;
31812         }
31813         
31814         var coords = [];
31815         
31816         for(var i = 0, finger; finger = touches[i]; i++){
31817             coords.push(finger.pageX, finger.pageY);
31818         }
31819         
31820         var x = Math.pow(coords[0] - coords[2], 2);
31821         var y = Math.pow(coords[1] - coords[3], 2);
31822         
31823         this.endDistance = Math.sqrt(x + y);
31824         
31825         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31826         
31827         if(!this.zoomable()){
31828             this.scale = this.startScale;
31829             return;
31830         }
31831         
31832         this.draw();
31833         
31834     },
31835     
31836     onTouchEnd : function(e)
31837     {
31838         this.pinching = false;
31839         this.dragable = false;
31840         
31841     },
31842     
31843     process : function(file, crop)
31844     {
31845         if(this.loadMask){
31846             this.maskEl.mask(this.loadingText);
31847         }
31848         
31849         this.xhr = new XMLHttpRequest();
31850         
31851         file.xhr = this.xhr;
31852
31853         this.xhr.open(this.method, this.url, true);
31854         
31855         var headers = {
31856             "Accept": "application/json",
31857             "Cache-Control": "no-cache",
31858             "X-Requested-With": "XMLHttpRequest"
31859         };
31860         
31861         for (var headerName in headers) {
31862             var headerValue = headers[headerName];
31863             if (headerValue) {
31864                 this.xhr.setRequestHeader(headerName, headerValue);
31865             }
31866         }
31867         
31868         var _this = this;
31869         
31870         this.xhr.onload = function()
31871         {
31872             _this.xhrOnLoad(_this.xhr);
31873         }
31874         
31875         this.xhr.onerror = function()
31876         {
31877             _this.xhrOnError(_this.xhr);
31878         }
31879         
31880         var formData = new FormData();
31881
31882         formData.append('returnHTML', 'NO');
31883         
31884         if(crop){
31885             formData.append('crop', crop);
31886         }
31887         
31888         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31889             formData.append(this.paramName, file, file.name);
31890         }
31891         
31892         if(typeof(file.filename) != 'undefined'){
31893             formData.append('filename', file.filename);
31894         }
31895         
31896         if(typeof(file.mimetype) != 'undefined'){
31897             formData.append('mimetype', file.mimetype);
31898         }
31899         
31900         if(this.fireEvent('arrange', this, formData) != false){
31901             this.xhr.send(formData);
31902         };
31903     },
31904     
31905     xhrOnLoad : function(xhr)
31906     {
31907         if(this.loadMask){
31908             this.maskEl.unmask();
31909         }
31910         
31911         if (xhr.readyState !== 4) {
31912             this.fireEvent('exception', this, xhr);
31913             return;
31914         }
31915
31916         var response = Roo.decode(xhr.responseText);
31917         
31918         if(!response.success){
31919             this.fireEvent('exception', this, xhr);
31920             return;
31921         }
31922         
31923         var response = Roo.decode(xhr.responseText);
31924         
31925         this.fireEvent('upload', this, response);
31926         
31927     },
31928     
31929     xhrOnError : function()
31930     {
31931         if(this.loadMask){
31932             this.maskEl.unmask();
31933         }
31934         
31935         Roo.log('xhr on error');
31936         
31937         var response = Roo.decode(xhr.responseText);
31938           
31939         Roo.log(response);
31940         
31941     },
31942     
31943     prepare : function(file)
31944     {   
31945         if(this.loadMask){
31946             this.maskEl.mask(this.loadingText);
31947         }
31948         
31949         this.file = false;
31950         this.exif = {};
31951         
31952         if(typeof(file) === 'string'){
31953             this.loadCanvas(file);
31954             return;
31955         }
31956         
31957         if(!file || !this.urlAPI){
31958             return;
31959         }
31960         
31961         this.file = file;
31962         this.cropType = file.type;
31963         
31964         var _this = this;
31965         
31966         if(this.fireEvent('prepare', this, this.file) != false){
31967             
31968             var reader = new FileReader();
31969             
31970             reader.onload = function (e) {
31971                 if (e.target.error) {
31972                     Roo.log(e.target.error);
31973                     return;
31974                 }
31975                 
31976                 var buffer = e.target.result,
31977                     dataView = new DataView(buffer),
31978                     offset = 2,
31979                     maxOffset = dataView.byteLength - 4,
31980                     markerBytes,
31981                     markerLength;
31982                 
31983                 if (dataView.getUint16(0) === 0xffd8) {
31984                     while (offset < maxOffset) {
31985                         markerBytes = dataView.getUint16(offset);
31986                         
31987                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31988                             markerLength = dataView.getUint16(offset + 2) + 2;
31989                             if (offset + markerLength > dataView.byteLength) {
31990                                 Roo.log('Invalid meta data: Invalid segment size.');
31991                                 break;
31992                             }
31993                             
31994                             if(markerBytes == 0xffe1){
31995                                 _this.parseExifData(
31996                                     dataView,
31997                                     offset,
31998                                     markerLength
31999                                 );
32000                             }
32001                             
32002                             offset += markerLength;
32003                             
32004                             continue;
32005                         }
32006                         
32007                         break;
32008                     }
32009                     
32010                 }
32011                 
32012                 var url = _this.urlAPI.createObjectURL(_this.file);
32013                 
32014                 _this.loadCanvas(url);
32015                 
32016                 return;
32017             }
32018             
32019             reader.readAsArrayBuffer(this.file);
32020             
32021         }
32022         
32023     },
32024     
32025     parseExifData : function(dataView, offset, length)
32026     {
32027         var tiffOffset = offset + 10,
32028             littleEndian,
32029             dirOffset;
32030     
32031         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32032             // No Exif data, might be XMP data instead
32033             return;
32034         }
32035         
32036         // Check for the ASCII code for "Exif" (0x45786966):
32037         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32038             // No Exif data, might be XMP data instead
32039             return;
32040         }
32041         if (tiffOffset + 8 > dataView.byteLength) {
32042             Roo.log('Invalid Exif data: Invalid segment size.');
32043             return;
32044         }
32045         // Check for the two null bytes:
32046         if (dataView.getUint16(offset + 8) !== 0x0000) {
32047             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32048             return;
32049         }
32050         // Check the byte alignment:
32051         switch (dataView.getUint16(tiffOffset)) {
32052         case 0x4949:
32053             littleEndian = true;
32054             break;
32055         case 0x4D4D:
32056             littleEndian = false;
32057             break;
32058         default:
32059             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32060             return;
32061         }
32062         // Check for the TIFF tag marker (0x002A):
32063         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32064             Roo.log('Invalid Exif data: Missing TIFF marker.');
32065             return;
32066         }
32067         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32068         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32069         
32070         this.parseExifTags(
32071             dataView,
32072             tiffOffset,
32073             tiffOffset + dirOffset,
32074             littleEndian
32075         );
32076     },
32077     
32078     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32079     {
32080         var tagsNumber,
32081             dirEndOffset,
32082             i;
32083         if (dirOffset + 6 > dataView.byteLength) {
32084             Roo.log('Invalid Exif data: Invalid directory offset.');
32085             return;
32086         }
32087         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32088         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32089         if (dirEndOffset + 4 > dataView.byteLength) {
32090             Roo.log('Invalid Exif data: Invalid directory size.');
32091             return;
32092         }
32093         for (i = 0; i < tagsNumber; i += 1) {
32094             this.parseExifTag(
32095                 dataView,
32096                 tiffOffset,
32097                 dirOffset + 2 + 12 * i, // tag offset
32098                 littleEndian
32099             );
32100         }
32101         // Return the offset to the next directory:
32102         return dataView.getUint32(dirEndOffset, littleEndian);
32103     },
32104     
32105     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32106     {
32107         var tag = dataView.getUint16(offset, littleEndian);
32108         
32109         this.exif[tag] = this.getExifValue(
32110             dataView,
32111             tiffOffset,
32112             offset,
32113             dataView.getUint16(offset + 2, littleEndian), // tag type
32114             dataView.getUint32(offset + 4, littleEndian), // tag length
32115             littleEndian
32116         );
32117     },
32118     
32119     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32120     {
32121         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32122             tagSize,
32123             dataOffset,
32124             values,
32125             i,
32126             str,
32127             c;
32128     
32129         if (!tagType) {
32130             Roo.log('Invalid Exif data: Invalid tag type.');
32131             return;
32132         }
32133         
32134         tagSize = tagType.size * length;
32135         // Determine if the value is contained in the dataOffset bytes,
32136         // or if the value at the dataOffset is a pointer to the actual data:
32137         dataOffset = tagSize > 4 ?
32138                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32139         if (dataOffset + tagSize > dataView.byteLength) {
32140             Roo.log('Invalid Exif data: Invalid data offset.');
32141             return;
32142         }
32143         if (length === 1) {
32144             return tagType.getValue(dataView, dataOffset, littleEndian);
32145         }
32146         values = [];
32147         for (i = 0; i < length; i += 1) {
32148             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32149         }
32150         
32151         if (tagType.ascii) {
32152             str = '';
32153             // Concatenate the chars:
32154             for (i = 0; i < values.length; i += 1) {
32155                 c = values[i];
32156                 // Ignore the terminating NULL byte(s):
32157                 if (c === '\u0000') {
32158                     break;
32159                 }
32160                 str += c;
32161             }
32162             return str;
32163         }
32164         return values;
32165     }
32166     
32167 });
32168
32169 Roo.apply(Roo.bootstrap.UploadCropbox, {
32170     tags : {
32171         'Orientation': 0x0112
32172     },
32173     
32174     Orientation: {
32175             1: 0, //'top-left',
32176 //            2: 'top-right',
32177             3: 180, //'bottom-right',
32178 //            4: 'bottom-left',
32179 //            5: 'left-top',
32180             6: 90, //'right-top',
32181 //            7: 'right-bottom',
32182             8: 270 //'left-bottom'
32183     },
32184     
32185     exifTagTypes : {
32186         // byte, 8-bit unsigned int:
32187         1: {
32188             getValue: function (dataView, dataOffset) {
32189                 return dataView.getUint8(dataOffset);
32190             },
32191             size: 1
32192         },
32193         // ascii, 8-bit byte:
32194         2: {
32195             getValue: function (dataView, dataOffset) {
32196                 return String.fromCharCode(dataView.getUint8(dataOffset));
32197             },
32198             size: 1,
32199             ascii: true
32200         },
32201         // short, 16 bit int:
32202         3: {
32203             getValue: function (dataView, dataOffset, littleEndian) {
32204                 return dataView.getUint16(dataOffset, littleEndian);
32205             },
32206             size: 2
32207         },
32208         // long, 32 bit int:
32209         4: {
32210             getValue: function (dataView, dataOffset, littleEndian) {
32211                 return dataView.getUint32(dataOffset, littleEndian);
32212             },
32213             size: 4
32214         },
32215         // rational = two long values, first is numerator, second is denominator:
32216         5: {
32217             getValue: function (dataView, dataOffset, littleEndian) {
32218                 return dataView.getUint32(dataOffset, littleEndian) /
32219                     dataView.getUint32(dataOffset + 4, littleEndian);
32220             },
32221             size: 8
32222         },
32223         // slong, 32 bit signed int:
32224         9: {
32225             getValue: function (dataView, dataOffset, littleEndian) {
32226                 return dataView.getInt32(dataOffset, littleEndian);
32227             },
32228             size: 4
32229         },
32230         // srational, two slongs, first is numerator, second is denominator:
32231         10: {
32232             getValue: function (dataView, dataOffset, littleEndian) {
32233                 return dataView.getInt32(dataOffset, littleEndian) /
32234                     dataView.getInt32(dataOffset + 4, littleEndian);
32235             },
32236             size: 8
32237         }
32238     },
32239     
32240     footer : {
32241         STANDARD : [
32242             {
32243                 tag : 'div',
32244                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32245                 action : 'rotate-left',
32246                 cn : [
32247                     {
32248                         tag : 'button',
32249                         cls : 'btn btn-default',
32250                         html : '<i class="fa fa-undo"></i>'
32251                     }
32252                 ]
32253             },
32254             {
32255                 tag : 'div',
32256                 cls : 'btn-group roo-upload-cropbox-picture',
32257                 action : 'picture',
32258                 cn : [
32259                     {
32260                         tag : 'button',
32261                         cls : 'btn btn-default',
32262                         html : '<i class="fa fa-picture-o"></i>'
32263                     }
32264                 ]
32265             },
32266             {
32267                 tag : 'div',
32268                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32269                 action : 'rotate-right',
32270                 cn : [
32271                     {
32272                         tag : 'button',
32273                         cls : 'btn btn-default',
32274                         html : '<i class="fa fa-repeat"></i>'
32275                     }
32276                 ]
32277             }
32278         ],
32279         DOCUMENT : [
32280             {
32281                 tag : 'div',
32282                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32283                 action : 'rotate-left',
32284                 cn : [
32285                     {
32286                         tag : 'button',
32287                         cls : 'btn btn-default',
32288                         html : '<i class="fa fa-undo"></i>'
32289                     }
32290                 ]
32291             },
32292             {
32293                 tag : 'div',
32294                 cls : 'btn-group roo-upload-cropbox-download',
32295                 action : 'download',
32296                 cn : [
32297                     {
32298                         tag : 'button',
32299                         cls : 'btn btn-default',
32300                         html : '<i class="fa fa-download"></i>'
32301                     }
32302                 ]
32303             },
32304             {
32305                 tag : 'div',
32306                 cls : 'btn-group roo-upload-cropbox-crop',
32307                 action : 'crop',
32308                 cn : [
32309                     {
32310                         tag : 'button',
32311                         cls : 'btn btn-default',
32312                         html : '<i class="fa fa-crop"></i>'
32313                     }
32314                 ]
32315             },
32316             {
32317                 tag : 'div',
32318                 cls : 'btn-group roo-upload-cropbox-trash',
32319                 action : 'trash',
32320                 cn : [
32321                     {
32322                         tag : 'button',
32323                         cls : 'btn btn-default',
32324                         html : '<i class="fa fa-trash"></i>'
32325                     }
32326                 ]
32327             },
32328             {
32329                 tag : 'div',
32330                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32331                 action : 'rotate-right',
32332                 cn : [
32333                     {
32334                         tag : 'button',
32335                         cls : 'btn btn-default',
32336                         html : '<i class="fa fa-repeat"></i>'
32337                     }
32338                 ]
32339             }
32340         ],
32341         ROTATOR : [
32342             {
32343                 tag : 'div',
32344                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32345                 action : 'rotate-left',
32346                 cn : [
32347                     {
32348                         tag : 'button',
32349                         cls : 'btn btn-default',
32350                         html : '<i class="fa fa-undo"></i>'
32351                     }
32352                 ]
32353             },
32354             {
32355                 tag : 'div',
32356                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32357                 action : 'rotate-right',
32358                 cn : [
32359                     {
32360                         tag : 'button',
32361                         cls : 'btn btn-default',
32362                         html : '<i class="fa fa-repeat"></i>'
32363                     }
32364                 ]
32365             }
32366         ]
32367     }
32368 });
32369
32370 /*
32371 * Licence: LGPL
32372 */
32373
32374 /**
32375  * @class Roo.bootstrap.DocumentManager
32376  * @extends Roo.bootstrap.Component
32377  * Bootstrap DocumentManager class
32378  * @cfg {String} paramName default 'imageUpload'
32379  * @cfg {String} toolTipName default 'filename'
32380  * @cfg {String} method default POST
32381  * @cfg {String} url action url
32382  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32383  * @cfg {Boolean} multiple multiple upload default true
32384  * @cfg {Number} thumbSize default 300
32385  * @cfg {String} fieldLabel
32386  * @cfg {Number} labelWidth default 4
32387  * @cfg {String} labelAlign (left|top) default left
32388  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32389 * @cfg {Number} labellg set the width of label (1-12)
32390  * @cfg {Number} labelmd set the width of label (1-12)
32391  * @cfg {Number} labelsm set the width of label (1-12)
32392  * @cfg {Number} labelxs set the width of label (1-12)
32393  * 
32394  * @constructor
32395  * Create a new DocumentManager
32396  * @param {Object} config The config object
32397  */
32398
32399 Roo.bootstrap.DocumentManager = function(config){
32400     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32401     
32402     this.files = [];
32403     this.delegates = [];
32404     
32405     this.addEvents({
32406         /**
32407          * @event initial
32408          * Fire when initial the DocumentManager
32409          * @param {Roo.bootstrap.DocumentManager} this
32410          */
32411         "initial" : true,
32412         /**
32413          * @event inspect
32414          * inspect selected file
32415          * @param {Roo.bootstrap.DocumentManager} this
32416          * @param {File} file
32417          */
32418         "inspect" : true,
32419         /**
32420          * @event exception
32421          * Fire when xhr load exception
32422          * @param {Roo.bootstrap.DocumentManager} this
32423          * @param {XMLHttpRequest} xhr
32424          */
32425         "exception" : true,
32426         /**
32427          * @event afterupload
32428          * Fire when xhr load exception
32429          * @param {Roo.bootstrap.DocumentManager} this
32430          * @param {XMLHttpRequest} xhr
32431          */
32432         "afterupload" : true,
32433         /**
32434          * @event prepare
32435          * prepare the form data
32436          * @param {Roo.bootstrap.DocumentManager} this
32437          * @param {Object} formData
32438          */
32439         "prepare" : true,
32440         /**
32441          * @event remove
32442          * Fire when remove the file
32443          * @param {Roo.bootstrap.DocumentManager} this
32444          * @param {Object} file
32445          */
32446         "remove" : true,
32447         /**
32448          * @event refresh
32449          * Fire after refresh the file
32450          * @param {Roo.bootstrap.DocumentManager} this
32451          */
32452         "refresh" : true,
32453         /**
32454          * @event click
32455          * Fire after click the image
32456          * @param {Roo.bootstrap.DocumentManager} this
32457          * @param {Object} file
32458          */
32459         "click" : true,
32460         /**
32461          * @event edit
32462          * Fire when upload a image and editable set to true
32463          * @param {Roo.bootstrap.DocumentManager} this
32464          * @param {Object} file
32465          */
32466         "edit" : true,
32467         /**
32468          * @event beforeselectfile
32469          * Fire before select file
32470          * @param {Roo.bootstrap.DocumentManager} this
32471          */
32472         "beforeselectfile" : true,
32473         /**
32474          * @event process
32475          * Fire before process file
32476          * @param {Roo.bootstrap.DocumentManager} this
32477          * @param {Object} file
32478          */
32479         "process" : true,
32480         /**
32481          * @event previewrendered
32482          * Fire when preview rendered
32483          * @param {Roo.bootstrap.DocumentManager} this
32484          * @param {Object} file
32485          */
32486         "previewrendered" : true,
32487         /**
32488          */
32489         "previewResize" : true
32490         
32491     });
32492 };
32493
32494 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32495     
32496     boxes : 0,
32497     inputName : '',
32498     thumbSize : 300,
32499     multiple : true,
32500     files : false,
32501     method : 'POST',
32502     url : '',
32503     paramName : 'imageUpload',
32504     toolTipName : 'filename',
32505     fieldLabel : '',
32506     labelWidth : 4,
32507     labelAlign : 'left',
32508     editable : true,
32509     delegates : false,
32510     xhr : false, 
32511     
32512     labellg : 0,
32513     labelmd : 0,
32514     labelsm : 0,
32515     labelxs : 0,
32516     
32517     getAutoCreate : function()
32518     {   
32519         var managerWidget = {
32520             tag : 'div',
32521             cls : 'roo-document-manager',
32522             cn : [
32523                 {
32524                     tag : 'input',
32525                     cls : 'roo-document-manager-selector',
32526                     type : 'file'
32527                 },
32528                 {
32529                     tag : 'div',
32530                     cls : 'roo-document-manager-uploader',
32531                     cn : [
32532                         {
32533                             tag : 'div',
32534                             cls : 'roo-document-manager-upload-btn',
32535                             html : '<i class="fa fa-plus"></i>'
32536                         }
32537                     ]
32538                     
32539                 }
32540             ]
32541         };
32542         
32543         var content = [
32544             {
32545                 tag : 'div',
32546                 cls : 'column col-md-12',
32547                 cn : managerWidget
32548             }
32549         ];
32550         
32551         if(this.fieldLabel.length){
32552             
32553             content = [
32554                 {
32555                     tag : 'div',
32556                     cls : 'column col-md-12',
32557                     html : this.fieldLabel
32558                 },
32559                 {
32560                     tag : 'div',
32561                     cls : 'column col-md-12',
32562                     cn : managerWidget
32563                 }
32564             ];
32565
32566             if(this.labelAlign == 'left'){
32567                 content = [
32568                     {
32569                         tag : 'div',
32570                         cls : 'column',
32571                         html : this.fieldLabel
32572                     },
32573                     {
32574                         tag : 'div',
32575                         cls : 'column',
32576                         cn : managerWidget
32577                     }
32578                 ];
32579                 
32580                 if(this.labelWidth > 12){
32581                     content[0].style = "width: " + this.labelWidth + 'px';
32582                 }
32583
32584                 if(this.labelWidth < 13 && this.labelmd == 0){
32585                     this.labelmd = this.labelWidth;
32586                 }
32587
32588                 if(this.labellg > 0){
32589                     content[0].cls += ' col-lg-' + this.labellg;
32590                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32591                 }
32592
32593                 if(this.labelmd > 0){
32594                     content[0].cls += ' col-md-' + this.labelmd;
32595                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32596                 }
32597
32598                 if(this.labelsm > 0){
32599                     content[0].cls += ' col-sm-' + this.labelsm;
32600                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32601                 }
32602
32603                 if(this.labelxs > 0){
32604                     content[0].cls += ' col-xs-' + this.labelxs;
32605                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32606                 }
32607                 
32608             }
32609         }
32610         
32611         var cfg = {
32612             tag : 'div',
32613             cls : 'row clearfix',
32614             cn : content
32615         };
32616         
32617         return cfg;
32618         
32619     },
32620     
32621     initEvents : function()
32622     {
32623         this.managerEl = this.el.select('.roo-document-manager', true).first();
32624         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32625         
32626         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32627         this.selectorEl.hide();
32628         
32629         if(this.multiple){
32630             this.selectorEl.attr('multiple', 'multiple');
32631         }
32632         
32633         this.selectorEl.on('change', this.onFileSelected, this);
32634         
32635         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32636         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32637         
32638         this.uploader.on('click', this.onUploaderClick, this);
32639         
32640         this.renderProgressDialog();
32641         
32642         var _this = this;
32643         
32644         window.addEventListener("resize", function() { _this.refresh(); } );
32645         
32646         this.fireEvent('initial', this);
32647     },
32648     
32649     renderProgressDialog : function()
32650     {
32651         var _this = this;
32652         
32653         this.progressDialog = new Roo.bootstrap.Modal({
32654             cls : 'roo-document-manager-progress-dialog',
32655             allow_close : false,
32656             animate : false,
32657             title : '',
32658             buttons : [
32659                 {
32660                     name  :'cancel',
32661                     weight : 'danger',
32662                     html : 'Cancel'
32663                 }
32664             ], 
32665             listeners : { 
32666                 btnclick : function() {
32667                     _this.uploadCancel();
32668                     this.hide();
32669                 }
32670             }
32671         });
32672          
32673         this.progressDialog.render(Roo.get(document.body));
32674          
32675         this.progress = new Roo.bootstrap.Progress({
32676             cls : 'roo-document-manager-progress',
32677             active : true,
32678             striped : true
32679         });
32680         
32681         this.progress.render(this.progressDialog.getChildContainer());
32682         
32683         this.progressBar = new Roo.bootstrap.ProgressBar({
32684             cls : 'roo-document-manager-progress-bar',
32685             aria_valuenow : 0,
32686             aria_valuemin : 0,
32687             aria_valuemax : 12,
32688             panel : 'success'
32689         });
32690         
32691         this.progressBar.render(this.progress.getChildContainer());
32692     },
32693     
32694     onUploaderClick : function(e)
32695     {
32696         e.preventDefault();
32697      
32698         if(this.fireEvent('beforeselectfile', this) != false){
32699             this.selectorEl.dom.click();
32700         }
32701         
32702     },
32703     
32704     onFileSelected : function(e)
32705     {
32706         e.preventDefault();
32707         
32708         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32709             return;
32710         }
32711         
32712         Roo.each(this.selectorEl.dom.files, function(file){
32713             if(this.fireEvent('inspect', this, file) != false){
32714                 this.files.push(file);
32715             }
32716         }, this);
32717         
32718         this.queue();
32719         
32720     },
32721     
32722     queue : function()
32723     {
32724         this.selectorEl.dom.value = '';
32725         
32726         if(!this.files || !this.files.length){
32727             return;
32728         }
32729         
32730         if(this.boxes > 0 && this.files.length > this.boxes){
32731             this.files = this.files.slice(0, this.boxes);
32732         }
32733         
32734         this.uploader.show();
32735         
32736         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32737             this.uploader.hide();
32738         }
32739         
32740         var _this = this;
32741         
32742         var files = [];
32743         
32744         var docs = [];
32745         
32746         Roo.each(this.files, function(file){
32747             
32748             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32749                 var f = this.renderPreview(file);
32750                 files.push(f);
32751                 return;
32752             }
32753             
32754             if(file.type.indexOf('image') != -1){
32755                 this.delegates.push(
32756                     (function(){
32757                         _this.process(file);
32758                     }).createDelegate(this)
32759                 );
32760         
32761                 return;
32762             }
32763             
32764             docs.push(
32765                 (function(){
32766                     _this.process(file);
32767                 }).createDelegate(this)
32768             );
32769             
32770         }, this);
32771         
32772         this.files = files;
32773         
32774         this.delegates = this.delegates.concat(docs);
32775         
32776         if(!this.delegates.length){
32777             this.refresh();
32778             return;
32779         }
32780         
32781         this.progressBar.aria_valuemax = this.delegates.length;
32782         
32783         this.arrange();
32784         
32785         return;
32786     },
32787     
32788     arrange : function()
32789     {
32790         if(!this.delegates.length){
32791             this.progressDialog.hide();
32792             this.refresh();
32793             return;
32794         }
32795         
32796         var delegate = this.delegates.shift();
32797         
32798         this.progressDialog.show();
32799         
32800         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32801         
32802         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32803         
32804         delegate();
32805     },
32806     
32807     refresh : function()
32808     {
32809         this.uploader.show();
32810         
32811         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32812             this.uploader.hide();
32813         }
32814         
32815         Roo.isTouch ? this.closable(false) : this.closable(true);
32816         
32817         this.fireEvent('refresh', this);
32818     },
32819     
32820     onRemove : function(e, el, o)
32821     {
32822         e.preventDefault();
32823         
32824         this.fireEvent('remove', this, o);
32825         
32826     },
32827     
32828     remove : function(o)
32829     {
32830         var files = [];
32831         
32832         Roo.each(this.files, function(file){
32833             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32834                 files.push(file);
32835                 return;
32836             }
32837
32838             o.target.remove();
32839
32840         }, this);
32841         
32842         this.files = files;
32843         
32844         this.refresh();
32845     },
32846     
32847     clear : function()
32848     {
32849         Roo.each(this.files, function(file){
32850             if(!file.target){
32851                 return;
32852             }
32853             
32854             file.target.remove();
32855
32856         }, this);
32857         
32858         this.files = [];
32859         
32860         this.refresh();
32861     },
32862     
32863     onClick : function(e, el, o)
32864     {
32865         e.preventDefault();
32866         
32867         this.fireEvent('click', this, o);
32868         
32869     },
32870     
32871     closable : function(closable)
32872     {
32873         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32874             
32875             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32876             
32877             if(closable){
32878                 el.show();
32879                 return;
32880             }
32881             
32882             el.hide();
32883             
32884         }, this);
32885     },
32886     
32887     xhrOnLoad : function(xhr)
32888     {
32889         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32890             el.remove();
32891         }, this);
32892         
32893         if (xhr.readyState !== 4) {
32894             this.arrange();
32895             this.fireEvent('exception', this, xhr);
32896             return;
32897         }
32898
32899         var response = Roo.decode(xhr.responseText);
32900         
32901         if(!response.success){
32902             this.arrange();
32903             this.fireEvent('exception', this, xhr);
32904             return;
32905         }
32906         
32907         var file = this.renderPreview(response.data);
32908         
32909         this.files.push(file);
32910         
32911         this.arrange();
32912         
32913         this.fireEvent('afterupload', this, xhr);
32914         
32915     },
32916     
32917     xhrOnError : function(xhr)
32918     {
32919         Roo.log('xhr on error');
32920         
32921         var response = Roo.decode(xhr.responseText);
32922           
32923         Roo.log(response);
32924         
32925         this.arrange();
32926     },
32927     
32928     process : function(file)
32929     {
32930         if(this.fireEvent('process', this, file) !== false){
32931             if(this.editable && file.type.indexOf('image') != -1){
32932                 this.fireEvent('edit', this, file);
32933                 return;
32934             }
32935
32936             this.uploadStart(file, false);
32937
32938             return;
32939         }
32940         
32941     },
32942     
32943     uploadStart : function(file, crop)
32944     {
32945         this.xhr = new XMLHttpRequest();
32946         
32947         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32948             this.arrange();
32949             return;
32950         }
32951         
32952         file.xhr = this.xhr;
32953             
32954         this.managerEl.createChild({
32955             tag : 'div',
32956             cls : 'roo-document-manager-loading',
32957             cn : [
32958                 {
32959                     tag : 'div',
32960                     tooltip : file.name,
32961                     cls : 'roo-document-manager-thumb',
32962                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32963                 }
32964             ]
32965
32966         });
32967
32968         this.xhr.open(this.method, this.url, true);
32969         
32970         var headers = {
32971             "Accept": "application/json",
32972             "Cache-Control": "no-cache",
32973             "X-Requested-With": "XMLHttpRequest"
32974         };
32975         
32976         for (var headerName in headers) {
32977             var headerValue = headers[headerName];
32978             if (headerValue) {
32979                 this.xhr.setRequestHeader(headerName, headerValue);
32980             }
32981         }
32982         
32983         var _this = this;
32984         
32985         this.xhr.onload = function()
32986         {
32987             _this.xhrOnLoad(_this.xhr);
32988         }
32989         
32990         this.xhr.onerror = function()
32991         {
32992             _this.xhrOnError(_this.xhr);
32993         }
32994         
32995         var formData = new FormData();
32996
32997         formData.append('returnHTML', 'NO');
32998         
32999         if(crop){
33000             formData.append('crop', crop);
33001         }
33002         
33003         formData.append(this.paramName, file, file.name);
33004         
33005         var options = {
33006             file : file, 
33007             manually : false
33008         };
33009         
33010         if(this.fireEvent('prepare', this, formData, options) != false){
33011             
33012             if(options.manually){
33013                 return;
33014             }
33015             
33016             this.xhr.send(formData);
33017             return;
33018         };
33019         
33020         this.uploadCancel();
33021     },
33022     
33023     uploadCancel : function()
33024     {
33025         if (this.xhr) {
33026             this.xhr.abort();
33027         }
33028         
33029         this.delegates = [];
33030         
33031         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33032             el.remove();
33033         }, this);
33034         
33035         this.arrange();
33036     },
33037     
33038     renderPreview : function(file)
33039     {
33040         if(typeof(file.target) != 'undefined' && file.target){
33041             return file;
33042         }
33043         
33044         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33045         
33046         var previewEl = this.managerEl.createChild({
33047             tag : 'div',
33048             cls : 'roo-document-manager-preview',
33049             cn : [
33050                 {
33051                     tag : 'div',
33052                     tooltip : file[this.toolTipName],
33053                     cls : 'roo-document-manager-thumb',
33054                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33055                 },
33056                 {
33057                     tag : 'button',
33058                     cls : 'close',
33059                     html : '<i class="fa fa-times-circle"></i>'
33060                 }
33061             ]
33062         });
33063
33064         var close = previewEl.select('button.close', true).first();
33065
33066         close.on('click', this.onRemove, this, file);
33067
33068         file.target = previewEl;
33069
33070         var image = previewEl.select('img', true).first();
33071         
33072         var _this = this;
33073         
33074         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33075         
33076         image.on('click', this.onClick, this, file);
33077         
33078         this.fireEvent('previewrendered', this, file);
33079         
33080         return file;
33081         
33082     },
33083     
33084     onPreviewLoad : function(file, image)
33085     {
33086         if(typeof(file.target) == 'undefined' || !file.target){
33087             return;
33088         }
33089         
33090         var width = image.dom.naturalWidth || image.dom.width;
33091         var height = image.dom.naturalHeight || image.dom.height;
33092         
33093         if(!this.previewResize) {
33094             return;
33095         }
33096         
33097         if(width > height){
33098             file.target.addClass('wide');
33099             return;
33100         }
33101         
33102         file.target.addClass('tall');
33103         return;
33104         
33105     },
33106     
33107     uploadFromSource : function(file, crop)
33108     {
33109         this.xhr = new XMLHttpRequest();
33110         
33111         this.managerEl.createChild({
33112             tag : 'div',
33113             cls : 'roo-document-manager-loading',
33114             cn : [
33115                 {
33116                     tag : 'div',
33117                     tooltip : file.name,
33118                     cls : 'roo-document-manager-thumb',
33119                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33120                 }
33121             ]
33122
33123         });
33124
33125         this.xhr.open(this.method, this.url, true);
33126         
33127         var headers = {
33128             "Accept": "application/json",
33129             "Cache-Control": "no-cache",
33130             "X-Requested-With": "XMLHttpRequest"
33131         };
33132         
33133         for (var headerName in headers) {
33134             var headerValue = headers[headerName];
33135             if (headerValue) {
33136                 this.xhr.setRequestHeader(headerName, headerValue);
33137             }
33138         }
33139         
33140         var _this = this;
33141         
33142         this.xhr.onload = function()
33143         {
33144             _this.xhrOnLoad(_this.xhr);
33145         }
33146         
33147         this.xhr.onerror = function()
33148         {
33149             _this.xhrOnError(_this.xhr);
33150         }
33151         
33152         var formData = new FormData();
33153
33154         formData.append('returnHTML', 'NO');
33155         
33156         formData.append('crop', crop);
33157         
33158         if(typeof(file.filename) != 'undefined'){
33159             formData.append('filename', file.filename);
33160         }
33161         
33162         if(typeof(file.mimetype) != 'undefined'){
33163             formData.append('mimetype', file.mimetype);
33164         }
33165         
33166         Roo.log(formData);
33167         
33168         if(this.fireEvent('prepare', this, formData) != false){
33169             this.xhr.send(formData);
33170         };
33171     }
33172 });
33173
33174 /*
33175 * Licence: LGPL
33176 */
33177
33178 /**
33179  * @class Roo.bootstrap.DocumentViewer
33180  * @extends Roo.bootstrap.Component
33181  * Bootstrap DocumentViewer class
33182  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33183  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33184  * 
33185  * @constructor
33186  * Create a new DocumentViewer
33187  * @param {Object} config The config object
33188  */
33189
33190 Roo.bootstrap.DocumentViewer = function(config){
33191     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33192     
33193     this.addEvents({
33194         /**
33195          * @event initial
33196          * Fire after initEvent
33197          * @param {Roo.bootstrap.DocumentViewer} this
33198          */
33199         "initial" : true,
33200         /**
33201          * @event click
33202          * Fire after click
33203          * @param {Roo.bootstrap.DocumentViewer} this
33204          */
33205         "click" : true,
33206         /**
33207          * @event download
33208          * Fire after download button
33209          * @param {Roo.bootstrap.DocumentViewer} this
33210          */
33211         "download" : true,
33212         /**
33213          * @event trash
33214          * Fire after trash button
33215          * @param {Roo.bootstrap.DocumentViewer} this
33216          */
33217         "trash" : true
33218         
33219     });
33220 };
33221
33222 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33223     
33224     showDownload : true,
33225     
33226     showTrash : true,
33227     
33228     getAutoCreate : function()
33229     {
33230         var cfg = {
33231             tag : 'div',
33232             cls : 'roo-document-viewer',
33233             cn : [
33234                 {
33235                     tag : 'div',
33236                     cls : 'roo-document-viewer-body',
33237                     cn : [
33238                         {
33239                             tag : 'div',
33240                             cls : 'roo-document-viewer-thumb',
33241                             cn : [
33242                                 {
33243                                     tag : 'img',
33244                                     cls : 'roo-document-viewer-image'
33245                                 }
33246                             ]
33247                         }
33248                     ]
33249                 },
33250                 {
33251                     tag : 'div',
33252                     cls : 'roo-document-viewer-footer',
33253                     cn : {
33254                         tag : 'div',
33255                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33256                         cn : [
33257                             {
33258                                 tag : 'div',
33259                                 cls : 'btn-group roo-document-viewer-download',
33260                                 cn : [
33261                                     {
33262                                         tag : 'button',
33263                                         cls : 'btn btn-default',
33264                                         html : '<i class="fa fa-download"></i>'
33265                                     }
33266                                 ]
33267                             },
33268                             {
33269                                 tag : 'div',
33270                                 cls : 'btn-group roo-document-viewer-trash',
33271                                 cn : [
33272                                     {
33273                                         tag : 'button',
33274                                         cls : 'btn btn-default',
33275                                         html : '<i class="fa fa-trash"></i>'
33276                                     }
33277                                 ]
33278                             }
33279                         ]
33280                     }
33281                 }
33282             ]
33283         };
33284         
33285         return cfg;
33286     },
33287     
33288     initEvents : function()
33289     {
33290         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33291         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33292         
33293         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33294         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33295         
33296         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33297         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33298         
33299         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33300         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33301         
33302         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33303         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33304         
33305         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33306         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33307         
33308         this.bodyEl.on('click', this.onClick, this);
33309         this.downloadBtn.on('click', this.onDownload, this);
33310         this.trashBtn.on('click', this.onTrash, this);
33311         
33312         this.downloadBtn.hide();
33313         this.trashBtn.hide();
33314         
33315         if(this.showDownload){
33316             this.downloadBtn.show();
33317         }
33318         
33319         if(this.showTrash){
33320             this.trashBtn.show();
33321         }
33322         
33323         if(!this.showDownload && !this.showTrash) {
33324             this.footerEl.hide();
33325         }
33326         
33327     },
33328     
33329     initial : function()
33330     {
33331         this.fireEvent('initial', this);
33332         
33333     },
33334     
33335     onClick : function(e)
33336     {
33337         e.preventDefault();
33338         
33339         this.fireEvent('click', this);
33340     },
33341     
33342     onDownload : function(e)
33343     {
33344         e.preventDefault();
33345         
33346         this.fireEvent('download', this);
33347     },
33348     
33349     onTrash : function(e)
33350     {
33351         e.preventDefault();
33352         
33353         this.fireEvent('trash', this);
33354     }
33355     
33356 });
33357 /*
33358  * - LGPL
33359  *
33360  * FieldLabel
33361  * 
33362  */
33363
33364 /**
33365  * @class Roo.bootstrap.FieldLabel
33366  * @extends Roo.bootstrap.Component
33367  * Bootstrap FieldLabel class
33368  * @cfg {String} html contents of the element
33369  * @cfg {String} tag tag of the element default label
33370  * @cfg {String} cls class of the element
33371  * @cfg {String} target label target 
33372  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33373  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33374  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33375  * @cfg {String} iconTooltip default "This field is required"
33376  * @cfg {String} indicatorpos (left|right) default left
33377  * 
33378  * @constructor
33379  * Create a new FieldLabel
33380  * @param {Object} config The config object
33381  */
33382
33383 Roo.bootstrap.FieldLabel = function(config){
33384     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33385     
33386     this.addEvents({
33387             /**
33388              * @event invalid
33389              * Fires after the field has been marked as invalid.
33390              * @param {Roo.form.FieldLabel} this
33391              * @param {String} msg The validation message
33392              */
33393             invalid : true,
33394             /**
33395              * @event valid
33396              * Fires after the field has been validated with no errors.
33397              * @param {Roo.form.FieldLabel} this
33398              */
33399             valid : true
33400         });
33401 };
33402
33403 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33404     
33405     tag: 'label',
33406     cls: '',
33407     html: '',
33408     target: '',
33409     allowBlank : true,
33410     invalidClass : 'has-warning',
33411     validClass : 'has-success',
33412     iconTooltip : 'This field is required',
33413     indicatorpos : 'left',
33414     
33415     getAutoCreate : function(){
33416         
33417         var cls = "";
33418         if (!this.allowBlank) {
33419             cls  = "visible";
33420         }
33421         
33422         var cfg = {
33423             tag : this.tag,
33424             cls : 'roo-bootstrap-field-label ' + this.cls,
33425             for : this.target,
33426             cn : [
33427                 {
33428                     tag : 'i',
33429                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33430                     tooltip : this.iconTooltip
33431                 },
33432                 {
33433                     tag : 'span',
33434                     html : this.html
33435                 }
33436             ] 
33437         };
33438         
33439         if(this.indicatorpos == 'right'){
33440             var cfg = {
33441                 tag : this.tag,
33442                 cls : 'roo-bootstrap-field-label ' + this.cls,
33443                 for : this.target,
33444                 cn : [
33445                     {
33446                         tag : 'span',
33447                         html : this.html
33448                     },
33449                     {
33450                         tag : 'i',
33451                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33452                         tooltip : this.iconTooltip
33453                     }
33454                 ] 
33455             };
33456         }
33457         
33458         return cfg;
33459     },
33460     
33461     initEvents: function() 
33462     {
33463         Roo.bootstrap.Element.superclass.initEvents.call(this);
33464         
33465         this.indicator = this.indicatorEl();
33466         
33467         if(this.indicator){
33468             this.indicator.removeClass('visible');
33469             this.indicator.addClass('invisible');
33470         }
33471         
33472         Roo.bootstrap.FieldLabel.register(this);
33473     },
33474     
33475     indicatorEl : function()
33476     {
33477         var indicator = this.el.select('i.roo-required-indicator',true).first();
33478         
33479         if(!indicator){
33480             return false;
33481         }
33482         
33483         return indicator;
33484         
33485     },
33486     
33487     /**
33488      * Mark this field as valid
33489      */
33490     markValid : function()
33491     {
33492         if(this.indicator){
33493             this.indicator.removeClass('visible');
33494             this.indicator.addClass('invisible');
33495         }
33496         if (Roo.bootstrap.version == 3) {
33497             this.el.removeClass(this.invalidClass);
33498             this.el.addClass(this.validClass);
33499         } else {
33500             this.el.removeClass('is-invalid');
33501             this.el.addClass('is-valid');
33502         }
33503         
33504         
33505         this.fireEvent('valid', this);
33506     },
33507     
33508     /**
33509      * Mark this field as invalid
33510      * @param {String} msg The validation message
33511      */
33512     markInvalid : function(msg)
33513     {
33514         if(this.indicator){
33515             this.indicator.removeClass('invisible');
33516             this.indicator.addClass('visible');
33517         }
33518           if (Roo.bootstrap.version == 3) {
33519             this.el.removeClass(this.validClass);
33520             this.el.addClass(this.invalidClass);
33521         } else {
33522             this.el.removeClass('is-valid');
33523             this.el.addClass('is-invalid');
33524         }
33525         
33526         
33527         this.fireEvent('invalid', this, msg);
33528     }
33529     
33530    
33531 });
33532
33533 Roo.apply(Roo.bootstrap.FieldLabel, {
33534     
33535     groups: {},
33536     
33537      /**
33538     * register a FieldLabel Group
33539     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33540     */
33541     register : function(label)
33542     {
33543         if(this.groups.hasOwnProperty(label.target)){
33544             return;
33545         }
33546      
33547         this.groups[label.target] = label;
33548         
33549     },
33550     /**
33551     * fetch a FieldLabel Group based on the target
33552     * @param {string} target
33553     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33554     */
33555     get: function(target) {
33556         if (typeof(this.groups[target]) == 'undefined') {
33557             return false;
33558         }
33559         
33560         return this.groups[target] ;
33561     }
33562 });
33563
33564  
33565
33566  /*
33567  * - LGPL
33568  *
33569  * page DateSplitField.
33570  * 
33571  */
33572
33573
33574 /**
33575  * @class Roo.bootstrap.DateSplitField
33576  * @extends Roo.bootstrap.Component
33577  * Bootstrap DateSplitField class
33578  * @cfg {string} fieldLabel - the label associated
33579  * @cfg {Number} labelWidth set the width of label (0-12)
33580  * @cfg {String} labelAlign (top|left)
33581  * @cfg {Boolean} dayAllowBlank (true|false) default false
33582  * @cfg {Boolean} monthAllowBlank (true|false) default false
33583  * @cfg {Boolean} yearAllowBlank (true|false) default false
33584  * @cfg {string} dayPlaceholder 
33585  * @cfg {string} monthPlaceholder
33586  * @cfg {string} yearPlaceholder
33587  * @cfg {string} dayFormat default 'd'
33588  * @cfg {string} monthFormat default 'm'
33589  * @cfg {string} yearFormat default 'Y'
33590  * @cfg {Number} labellg set the width of label (1-12)
33591  * @cfg {Number} labelmd set the width of label (1-12)
33592  * @cfg {Number} labelsm set the width of label (1-12)
33593  * @cfg {Number} labelxs set the width of label (1-12)
33594
33595  *     
33596  * @constructor
33597  * Create a new DateSplitField
33598  * @param {Object} config The config object
33599  */
33600
33601 Roo.bootstrap.DateSplitField = function(config){
33602     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33603     
33604     this.addEvents({
33605         // raw events
33606          /**
33607          * @event years
33608          * getting the data of years
33609          * @param {Roo.bootstrap.DateSplitField} this
33610          * @param {Object} years
33611          */
33612         "years" : true,
33613         /**
33614          * @event days
33615          * getting the data of days
33616          * @param {Roo.bootstrap.DateSplitField} this
33617          * @param {Object} days
33618          */
33619         "days" : true,
33620         /**
33621          * @event invalid
33622          * Fires after the field has been marked as invalid.
33623          * @param {Roo.form.Field} this
33624          * @param {String} msg The validation message
33625          */
33626         invalid : true,
33627        /**
33628          * @event valid
33629          * Fires after the field has been validated with no errors.
33630          * @param {Roo.form.Field} this
33631          */
33632         valid : true
33633     });
33634 };
33635
33636 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33637     
33638     fieldLabel : '',
33639     labelAlign : 'top',
33640     labelWidth : 3,
33641     dayAllowBlank : false,
33642     monthAllowBlank : false,
33643     yearAllowBlank : false,
33644     dayPlaceholder : '',
33645     monthPlaceholder : '',
33646     yearPlaceholder : '',
33647     dayFormat : 'd',
33648     monthFormat : 'm',
33649     yearFormat : 'Y',
33650     isFormField : true,
33651     labellg : 0,
33652     labelmd : 0,
33653     labelsm : 0,
33654     labelxs : 0,
33655     
33656     getAutoCreate : function()
33657     {
33658         var cfg = {
33659             tag : 'div',
33660             cls : 'row roo-date-split-field-group',
33661             cn : [
33662                 {
33663                     tag : 'input',
33664                     type : 'hidden',
33665                     cls : 'form-hidden-field roo-date-split-field-group-value',
33666                     name : this.name
33667                 }
33668             ]
33669         };
33670         
33671         var labelCls = 'col-md-12';
33672         var contentCls = 'col-md-4';
33673         
33674         if(this.fieldLabel){
33675             
33676             var label = {
33677                 tag : 'div',
33678                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33679                 cn : [
33680                     {
33681                         tag : 'label',
33682                         html : this.fieldLabel
33683                     }
33684                 ]
33685             };
33686             
33687             if(this.labelAlign == 'left'){
33688             
33689                 if(this.labelWidth > 12){
33690                     label.style = "width: " + this.labelWidth + 'px';
33691                 }
33692
33693                 if(this.labelWidth < 13 && this.labelmd == 0){
33694                     this.labelmd = this.labelWidth;
33695                 }
33696
33697                 if(this.labellg > 0){
33698                     labelCls = ' col-lg-' + this.labellg;
33699                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33700                 }
33701
33702                 if(this.labelmd > 0){
33703                     labelCls = ' col-md-' + this.labelmd;
33704                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33705                 }
33706
33707                 if(this.labelsm > 0){
33708                     labelCls = ' col-sm-' + this.labelsm;
33709                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33710                 }
33711
33712                 if(this.labelxs > 0){
33713                     labelCls = ' col-xs-' + this.labelxs;
33714                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33715                 }
33716             }
33717             
33718             label.cls += ' ' + labelCls;
33719             
33720             cfg.cn.push(label);
33721         }
33722         
33723         Roo.each(['day', 'month', 'year'], function(t){
33724             cfg.cn.push({
33725                 tag : 'div',
33726                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33727             });
33728         }, this);
33729         
33730         return cfg;
33731     },
33732     
33733     inputEl: function ()
33734     {
33735         return this.el.select('.roo-date-split-field-group-value', true).first();
33736     },
33737     
33738     onRender : function(ct, position) 
33739     {
33740         var _this = this;
33741         
33742         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33743         
33744         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33745         
33746         this.dayField = new Roo.bootstrap.ComboBox({
33747             allowBlank : this.dayAllowBlank,
33748             alwaysQuery : true,
33749             displayField : 'value',
33750             editable : false,
33751             fieldLabel : '',
33752             forceSelection : true,
33753             mode : 'local',
33754             placeholder : this.dayPlaceholder,
33755             selectOnFocus : true,
33756             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33757             triggerAction : 'all',
33758             typeAhead : true,
33759             valueField : 'value',
33760             store : new Roo.data.SimpleStore({
33761                 data : (function() {    
33762                     var days = [];
33763                     _this.fireEvent('days', _this, days);
33764                     return days;
33765                 })(),
33766                 fields : [ 'value' ]
33767             }),
33768             listeners : {
33769                 select : function (_self, record, index)
33770                 {
33771                     _this.setValue(_this.getValue());
33772                 }
33773             }
33774         });
33775
33776         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33777         
33778         this.monthField = new Roo.bootstrap.MonthField({
33779             after : '<i class=\"fa fa-calendar\"></i>',
33780             allowBlank : this.monthAllowBlank,
33781             placeholder : this.monthPlaceholder,
33782             readOnly : true,
33783             listeners : {
33784                 render : function (_self)
33785                 {
33786                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33787                         e.preventDefault();
33788                         _self.focus();
33789                     });
33790                 },
33791                 select : function (_self, oldvalue, newvalue)
33792                 {
33793                     _this.setValue(_this.getValue());
33794                 }
33795             }
33796         });
33797         
33798         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33799         
33800         this.yearField = new Roo.bootstrap.ComboBox({
33801             allowBlank : this.yearAllowBlank,
33802             alwaysQuery : true,
33803             displayField : 'value',
33804             editable : false,
33805             fieldLabel : '',
33806             forceSelection : true,
33807             mode : 'local',
33808             placeholder : this.yearPlaceholder,
33809             selectOnFocus : true,
33810             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33811             triggerAction : 'all',
33812             typeAhead : true,
33813             valueField : 'value',
33814             store : new Roo.data.SimpleStore({
33815                 data : (function() {
33816                     var years = [];
33817                     _this.fireEvent('years', _this, years);
33818                     return years;
33819                 })(),
33820                 fields : [ 'value' ]
33821             }),
33822             listeners : {
33823                 select : function (_self, record, index)
33824                 {
33825                     _this.setValue(_this.getValue());
33826                 }
33827             }
33828         });
33829
33830         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33831     },
33832     
33833     setValue : function(v, format)
33834     {
33835         this.inputEl.dom.value = v;
33836         
33837         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33838         
33839         var d = Date.parseDate(v, f);
33840         
33841         if(!d){
33842             this.validate();
33843             return;
33844         }
33845         
33846         this.setDay(d.format(this.dayFormat));
33847         this.setMonth(d.format(this.monthFormat));
33848         this.setYear(d.format(this.yearFormat));
33849         
33850         this.validate();
33851         
33852         return;
33853     },
33854     
33855     setDay : function(v)
33856     {
33857         this.dayField.setValue(v);
33858         this.inputEl.dom.value = this.getValue();
33859         this.validate();
33860         return;
33861     },
33862     
33863     setMonth : function(v)
33864     {
33865         this.monthField.setValue(v, true);
33866         this.inputEl.dom.value = this.getValue();
33867         this.validate();
33868         return;
33869     },
33870     
33871     setYear : function(v)
33872     {
33873         this.yearField.setValue(v);
33874         this.inputEl.dom.value = this.getValue();
33875         this.validate();
33876         return;
33877     },
33878     
33879     getDay : function()
33880     {
33881         return this.dayField.getValue();
33882     },
33883     
33884     getMonth : function()
33885     {
33886         return this.monthField.getValue();
33887     },
33888     
33889     getYear : function()
33890     {
33891         return this.yearField.getValue();
33892     },
33893     
33894     getValue : function()
33895     {
33896         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33897         
33898         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33899         
33900         return date;
33901     },
33902     
33903     reset : function()
33904     {
33905         this.setDay('');
33906         this.setMonth('');
33907         this.setYear('');
33908         this.inputEl.dom.value = '';
33909         this.validate();
33910         return;
33911     },
33912     
33913     validate : function()
33914     {
33915         var d = this.dayField.validate();
33916         var m = this.monthField.validate();
33917         var y = this.yearField.validate();
33918         
33919         var valid = true;
33920         
33921         if(
33922                 (!this.dayAllowBlank && !d) ||
33923                 (!this.monthAllowBlank && !m) ||
33924                 (!this.yearAllowBlank && !y)
33925         ){
33926             valid = false;
33927         }
33928         
33929         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33930             return valid;
33931         }
33932         
33933         if(valid){
33934             this.markValid();
33935             return valid;
33936         }
33937         
33938         this.markInvalid();
33939         
33940         return valid;
33941     },
33942     
33943     markValid : function()
33944     {
33945         
33946         var label = this.el.select('label', true).first();
33947         var icon = this.el.select('i.fa-star', true).first();
33948
33949         if(label && icon){
33950             icon.remove();
33951         }
33952         
33953         this.fireEvent('valid', this);
33954     },
33955     
33956      /**
33957      * Mark this field as invalid
33958      * @param {String} msg The validation message
33959      */
33960     markInvalid : function(msg)
33961     {
33962         
33963         var label = this.el.select('label', true).first();
33964         var icon = this.el.select('i.fa-star', true).first();
33965
33966         if(label && !icon){
33967             this.el.select('.roo-date-split-field-label', true).createChild({
33968                 tag : 'i',
33969                 cls : 'text-danger fa fa-lg fa-star',
33970                 tooltip : 'This field is required',
33971                 style : 'margin-right:5px;'
33972             }, label, true);
33973         }
33974         
33975         this.fireEvent('invalid', this, msg);
33976     },
33977     
33978     clearInvalid : function()
33979     {
33980         var label = this.el.select('label', true).first();
33981         var icon = this.el.select('i.fa-star', true).first();
33982
33983         if(label && icon){
33984             icon.remove();
33985         }
33986         
33987         this.fireEvent('valid', this);
33988     },
33989     
33990     getName: function()
33991     {
33992         return this.name;
33993     }
33994     
33995 });
33996
33997  
33998
33999 /**
34000  * @class Roo.bootstrap.LayoutMasonry
34001  * @extends Roo.bootstrap.Component
34002  * @children Roo.bootstrap.Element Roo.bootstrap.Image Roo.bootstrap.MasonryBrick
34003  * Bootstrap Layout Masonry class
34004  *
34005  * This is based on 
34006  * http://masonry.desandro.com
34007  *
34008  * The idea is to render all the bricks based on vertical width...
34009  *
34010  * The original code extends 'outlayer' - we might need to use that....
34011
34012  * @constructor
34013  * Create a new Element
34014  * @param {Object} config The config object
34015  */
34016
34017 Roo.bootstrap.LayoutMasonry = function(config){
34018     
34019     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34020     
34021     this.bricks = [];
34022     
34023     Roo.bootstrap.LayoutMasonry.register(this);
34024     
34025     this.addEvents({
34026         // raw events
34027         /**
34028          * @event layout
34029          * Fire after layout the items
34030          * @param {Roo.bootstrap.LayoutMasonry} this
34031          * @param {Roo.EventObject} e
34032          */
34033         "layout" : true
34034     });
34035     
34036 };
34037
34038 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34039     
34040     /**
34041      * @cfg {Boolean} isLayoutInstant = no animation?
34042      */   
34043     isLayoutInstant : false, // needed?
34044    
34045     /**
34046      * @cfg {Number} boxWidth  width of the columns
34047      */   
34048     boxWidth : 450,
34049     
34050       /**
34051      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34052      */   
34053     boxHeight : 0,
34054     
34055     /**
34056      * @cfg {Number} padWidth padding below box..
34057      */   
34058     padWidth : 10, 
34059     
34060     /**
34061      * @cfg {Number} gutter gutter width..
34062      */   
34063     gutter : 10,
34064     
34065      /**
34066      * @cfg {Number} maxCols maximum number of columns
34067      */   
34068     
34069     maxCols: 0,
34070     
34071     /**
34072      * @cfg {Boolean} isAutoInitial defalut true
34073      */   
34074     isAutoInitial : true, 
34075     
34076     containerWidth: 0,
34077     
34078     /**
34079      * @cfg {Boolean} isHorizontal defalut false
34080      */   
34081     isHorizontal : false, 
34082
34083     currentSize : null,
34084     
34085     tag: 'div',
34086     
34087     cls: '',
34088     
34089     bricks: null, //CompositeElement
34090     
34091     cols : 1,
34092     
34093     _isLayoutInited : false,
34094     
34095 //    isAlternative : false, // only use for vertical layout...
34096     
34097     /**
34098      * @cfg {Number} alternativePadWidth padding below box..
34099      */   
34100     alternativePadWidth : 50,
34101     
34102     selectedBrick : [],
34103     
34104     getAutoCreate : function(){
34105         
34106         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34107         
34108         var cfg = {
34109             tag: this.tag,
34110             cls: 'blog-masonary-wrapper ' + this.cls,
34111             cn : {
34112                 cls : 'mas-boxes masonary'
34113             }
34114         };
34115         
34116         return cfg;
34117     },
34118     
34119     getChildContainer: function( )
34120     {
34121         if (this.boxesEl) {
34122             return this.boxesEl;
34123         }
34124         
34125         this.boxesEl = this.el.select('.mas-boxes').first();
34126         
34127         return this.boxesEl;
34128     },
34129     
34130     
34131     initEvents : function()
34132     {
34133         var _this = this;
34134         
34135         if(this.isAutoInitial){
34136             Roo.log('hook children rendered');
34137             this.on('childrenrendered', function() {
34138                 Roo.log('children rendered');
34139                 _this.initial();
34140             } ,this);
34141         }
34142     },
34143     
34144     initial : function()
34145     {
34146         this.selectedBrick = [];
34147         
34148         this.currentSize = this.el.getBox(true);
34149         
34150         Roo.EventManager.onWindowResize(this.resize, this); 
34151
34152         if(!this.isAutoInitial){
34153             this.layout();
34154             return;
34155         }
34156         
34157         this.layout();
34158         
34159         return;
34160         //this.layout.defer(500,this);
34161         
34162     },
34163     
34164     resize : function()
34165     {
34166         var cs = this.el.getBox(true);
34167         
34168         if (
34169                 this.currentSize.width == cs.width && 
34170                 this.currentSize.x == cs.x && 
34171                 this.currentSize.height == cs.height && 
34172                 this.currentSize.y == cs.y 
34173         ) {
34174             Roo.log("no change in with or X or Y");
34175             return;
34176         }
34177         
34178         this.currentSize = cs;
34179         
34180         this.layout();
34181         
34182     },
34183     
34184     layout : function()
34185     {   
34186         this._resetLayout();
34187         
34188         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34189         
34190         this.layoutItems( isInstant );
34191       
34192         this._isLayoutInited = true;
34193         
34194         this.fireEvent('layout', this);
34195         
34196     },
34197     
34198     _resetLayout : function()
34199     {
34200         if(this.isHorizontal){
34201             this.horizontalMeasureColumns();
34202             return;
34203         }
34204         
34205         this.verticalMeasureColumns();
34206         
34207     },
34208     
34209     verticalMeasureColumns : function()
34210     {
34211         this.getContainerWidth();
34212         
34213 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34214 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34215 //            return;
34216 //        }
34217         
34218         var boxWidth = this.boxWidth + this.padWidth;
34219         
34220         if(this.containerWidth < this.boxWidth){
34221             boxWidth = this.containerWidth
34222         }
34223         
34224         var containerWidth = this.containerWidth;
34225         
34226         var cols = Math.floor(containerWidth / boxWidth);
34227         
34228         this.cols = Math.max( cols, 1 );
34229         
34230         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34231         
34232         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34233         
34234         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34235         
34236         this.colWidth = boxWidth + avail - this.padWidth;
34237         
34238         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34239         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34240     },
34241     
34242     horizontalMeasureColumns : function()
34243     {
34244         this.getContainerWidth();
34245         
34246         var boxWidth = this.boxWidth;
34247         
34248         if(this.containerWidth < boxWidth){
34249             boxWidth = this.containerWidth;
34250         }
34251         
34252         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34253         
34254         this.el.setHeight(boxWidth);
34255         
34256     },
34257     
34258     getContainerWidth : function()
34259     {
34260         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34261     },
34262     
34263     layoutItems : function( isInstant )
34264     {
34265         Roo.log(this.bricks);
34266         
34267         var items = Roo.apply([], this.bricks);
34268         
34269         if(this.isHorizontal){
34270             this._horizontalLayoutItems( items , isInstant );
34271             return;
34272         }
34273         
34274 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34275 //            this._verticalAlternativeLayoutItems( items , isInstant );
34276 //            return;
34277 //        }
34278         
34279         this._verticalLayoutItems( items , isInstant );
34280         
34281     },
34282     
34283     _verticalLayoutItems : function ( items , isInstant)
34284     {
34285         if ( !items || !items.length ) {
34286             return;
34287         }
34288         
34289         var standard = [
34290             ['xs', 'xs', 'xs', 'tall'],
34291             ['xs', 'xs', 'tall'],
34292             ['xs', 'xs', 'sm'],
34293             ['xs', 'xs', 'xs'],
34294             ['xs', 'tall'],
34295             ['xs', 'sm'],
34296             ['xs', 'xs'],
34297             ['xs'],
34298             
34299             ['sm', 'xs', 'xs'],
34300             ['sm', 'xs'],
34301             ['sm'],
34302             
34303             ['tall', 'xs', 'xs', 'xs'],
34304             ['tall', 'xs', 'xs'],
34305             ['tall', 'xs'],
34306             ['tall']
34307             
34308         ];
34309         
34310         var queue = [];
34311         
34312         var boxes = [];
34313         
34314         var box = [];
34315         
34316         Roo.each(items, function(item, k){
34317             
34318             switch (item.size) {
34319                 // these layouts take up a full box,
34320                 case 'md' :
34321                 case 'md-left' :
34322                 case 'md-right' :
34323                 case 'wide' :
34324                     
34325                     if(box.length){
34326                         boxes.push(box);
34327                         box = [];
34328                     }
34329                     
34330                     boxes.push([item]);
34331                     
34332                     break;
34333                     
34334                 case 'xs' :
34335                 case 'sm' :
34336                 case 'tall' :
34337                     
34338                     box.push(item);
34339                     
34340                     break;
34341                 default :
34342                     break;
34343                     
34344             }
34345             
34346         }, this);
34347         
34348         if(box.length){
34349             boxes.push(box);
34350             box = [];
34351         }
34352         
34353         var filterPattern = function(box, length)
34354         {
34355             if(!box.length){
34356                 return;
34357             }
34358             
34359             var match = false;
34360             
34361             var pattern = box.slice(0, length);
34362             
34363             var format = [];
34364             
34365             Roo.each(pattern, function(i){
34366                 format.push(i.size);
34367             }, this);
34368             
34369             Roo.each(standard, function(s){
34370                 
34371                 if(String(s) != String(format)){
34372                     return;
34373                 }
34374                 
34375                 match = true;
34376                 return false;
34377                 
34378             }, this);
34379             
34380             if(!match && length == 1){
34381                 return;
34382             }
34383             
34384             if(!match){
34385                 filterPattern(box, length - 1);
34386                 return;
34387             }
34388                 
34389             queue.push(pattern);
34390
34391             box = box.slice(length, box.length);
34392
34393             filterPattern(box, 4);
34394
34395             return;
34396             
34397         }
34398         
34399         Roo.each(boxes, function(box, k){
34400             
34401             if(!box.length){
34402                 return;
34403             }
34404             
34405             if(box.length == 1){
34406                 queue.push(box);
34407                 return;
34408             }
34409             
34410             filterPattern(box, 4);
34411             
34412         }, this);
34413         
34414         this._processVerticalLayoutQueue( queue, isInstant );
34415         
34416     },
34417     
34418 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34419 //    {
34420 //        if ( !items || !items.length ) {
34421 //            return;
34422 //        }
34423 //
34424 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34425 //        
34426 //    },
34427     
34428     _horizontalLayoutItems : function ( items , isInstant)
34429     {
34430         if ( !items || !items.length || items.length < 3) {
34431             return;
34432         }
34433         
34434         items.reverse();
34435         
34436         var eItems = items.slice(0, 3);
34437         
34438         items = items.slice(3, items.length);
34439         
34440         var standard = [
34441             ['xs', 'xs', 'xs', 'wide'],
34442             ['xs', 'xs', 'wide'],
34443             ['xs', 'xs', 'sm'],
34444             ['xs', 'xs', 'xs'],
34445             ['xs', 'wide'],
34446             ['xs', 'sm'],
34447             ['xs', 'xs'],
34448             ['xs'],
34449             
34450             ['sm', 'xs', 'xs'],
34451             ['sm', 'xs'],
34452             ['sm'],
34453             
34454             ['wide', 'xs', 'xs', 'xs'],
34455             ['wide', 'xs', 'xs'],
34456             ['wide', 'xs'],
34457             ['wide'],
34458             
34459             ['wide-thin']
34460         ];
34461         
34462         var queue = [];
34463         
34464         var boxes = [];
34465         
34466         var box = [];
34467         
34468         Roo.each(items, function(item, k){
34469             
34470             switch (item.size) {
34471                 case 'md' :
34472                 case 'md-left' :
34473                 case 'md-right' :
34474                 case 'tall' :
34475                     
34476                     if(box.length){
34477                         boxes.push(box);
34478                         box = [];
34479                     }
34480                     
34481                     boxes.push([item]);
34482                     
34483                     break;
34484                     
34485                 case 'xs' :
34486                 case 'sm' :
34487                 case 'wide' :
34488                 case 'wide-thin' :
34489                     
34490                     box.push(item);
34491                     
34492                     break;
34493                 default :
34494                     break;
34495                     
34496             }
34497             
34498         }, this);
34499         
34500         if(box.length){
34501             boxes.push(box);
34502             box = [];
34503         }
34504         
34505         var filterPattern = function(box, length)
34506         {
34507             if(!box.length){
34508                 return;
34509             }
34510             
34511             var match = false;
34512             
34513             var pattern = box.slice(0, length);
34514             
34515             var format = [];
34516             
34517             Roo.each(pattern, function(i){
34518                 format.push(i.size);
34519             }, this);
34520             
34521             Roo.each(standard, function(s){
34522                 
34523                 if(String(s) != String(format)){
34524                     return;
34525                 }
34526                 
34527                 match = true;
34528                 return false;
34529                 
34530             }, this);
34531             
34532             if(!match && length == 1){
34533                 return;
34534             }
34535             
34536             if(!match){
34537                 filterPattern(box, length - 1);
34538                 return;
34539             }
34540                 
34541             queue.push(pattern);
34542
34543             box = box.slice(length, box.length);
34544
34545             filterPattern(box, 4);
34546
34547             return;
34548             
34549         }
34550         
34551         Roo.each(boxes, function(box, k){
34552             
34553             if(!box.length){
34554                 return;
34555             }
34556             
34557             if(box.length == 1){
34558                 queue.push(box);
34559                 return;
34560             }
34561             
34562             filterPattern(box, 4);
34563             
34564         }, this);
34565         
34566         
34567         var prune = [];
34568         
34569         var pos = this.el.getBox(true);
34570         
34571         var minX = pos.x;
34572         
34573         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34574         
34575         var hit_end = false;
34576         
34577         Roo.each(queue, function(box){
34578             
34579             if(hit_end){
34580                 
34581                 Roo.each(box, function(b){
34582                 
34583                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34584                     b.el.hide();
34585
34586                 }, this);
34587
34588                 return;
34589             }
34590             
34591             var mx = 0;
34592             
34593             Roo.each(box, function(b){
34594                 
34595                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34596                 b.el.show();
34597
34598                 mx = Math.max(mx, b.x);
34599                 
34600             }, this);
34601             
34602             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34603             
34604             if(maxX < minX){
34605                 
34606                 Roo.each(box, function(b){
34607                 
34608                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34609                     b.el.hide();
34610                     
34611                 }, this);
34612                 
34613                 hit_end = true;
34614                 
34615                 return;
34616             }
34617             
34618             prune.push(box);
34619             
34620         }, this);
34621         
34622         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34623     },
34624     
34625     /** Sets position of item in DOM
34626     * @param {Element} item
34627     * @param {Number} x - horizontal position
34628     * @param {Number} y - vertical position
34629     * @param {Boolean} isInstant - disables transitions
34630     */
34631     _processVerticalLayoutQueue : function( queue, isInstant )
34632     {
34633         var pos = this.el.getBox(true);
34634         var x = pos.x;
34635         var y = pos.y;
34636         var maxY = [];
34637         
34638         for (var i = 0; i < this.cols; i++){
34639             maxY[i] = pos.y;
34640         }
34641         
34642         Roo.each(queue, function(box, k){
34643             
34644             var col = k % this.cols;
34645             
34646             Roo.each(box, function(b,kk){
34647                 
34648                 b.el.position('absolute');
34649                 
34650                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34651                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34652                 
34653                 if(b.size == 'md-left' || b.size == 'md-right'){
34654                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34655                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34656                 }
34657                 
34658                 b.el.setWidth(width);
34659                 b.el.setHeight(height);
34660                 // iframe?
34661                 b.el.select('iframe',true).setSize(width,height);
34662                 
34663             }, this);
34664             
34665             for (var i = 0; i < this.cols; i++){
34666                 
34667                 if(maxY[i] < maxY[col]){
34668                     col = i;
34669                     continue;
34670                 }
34671                 
34672                 col = Math.min(col, i);
34673                 
34674             }
34675             
34676             x = pos.x + col * (this.colWidth + this.padWidth);
34677             
34678             y = maxY[col];
34679             
34680             var positions = [];
34681             
34682             switch (box.length){
34683                 case 1 :
34684                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34685                     break;
34686                 case 2 :
34687                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34688                     break;
34689                 case 3 :
34690                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34691                     break;
34692                 case 4 :
34693                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34694                     break;
34695                 default :
34696                     break;
34697             }
34698             
34699             Roo.each(box, function(b,kk){
34700                 
34701                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34702                 
34703                 var sz = b.el.getSize();
34704                 
34705                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34706                 
34707             }, this);
34708             
34709         }, this);
34710         
34711         var mY = 0;
34712         
34713         for (var i = 0; i < this.cols; i++){
34714             mY = Math.max(mY, maxY[i]);
34715         }
34716         
34717         this.el.setHeight(mY - pos.y);
34718         
34719     },
34720     
34721 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34722 //    {
34723 //        var pos = this.el.getBox(true);
34724 //        var x = pos.x;
34725 //        var y = pos.y;
34726 //        var maxX = pos.right;
34727 //        
34728 //        var maxHeight = 0;
34729 //        
34730 //        Roo.each(items, function(item, k){
34731 //            
34732 //            var c = k % 2;
34733 //            
34734 //            item.el.position('absolute');
34735 //                
34736 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34737 //
34738 //            item.el.setWidth(width);
34739 //
34740 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34741 //
34742 //            item.el.setHeight(height);
34743 //            
34744 //            if(c == 0){
34745 //                item.el.setXY([x, y], isInstant ? false : true);
34746 //            } else {
34747 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34748 //            }
34749 //            
34750 //            y = y + height + this.alternativePadWidth;
34751 //            
34752 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34753 //            
34754 //        }, this);
34755 //        
34756 //        this.el.setHeight(maxHeight);
34757 //        
34758 //    },
34759     
34760     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34761     {
34762         var pos = this.el.getBox(true);
34763         
34764         var minX = pos.x;
34765         var minY = pos.y;
34766         
34767         var maxX = pos.right;
34768         
34769         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34770         
34771         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34772         
34773         Roo.each(queue, function(box, k){
34774             
34775             Roo.each(box, function(b, kk){
34776                 
34777                 b.el.position('absolute');
34778                 
34779                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34780                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34781                 
34782                 if(b.size == 'md-left' || b.size == 'md-right'){
34783                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34784                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34785                 }
34786                 
34787                 b.el.setWidth(width);
34788                 b.el.setHeight(height);
34789                 
34790             }, this);
34791             
34792             if(!box.length){
34793                 return;
34794             }
34795             
34796             var positions = [];
34797             
34798             switch (box.length){
34799                 case 1 :
34800                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34801                     break;
34802                 case 2 :
34803                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34804                     break;
34805                 case 3 :
34806                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34807                     break;
34808                 case 4 :
34809                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34810                     break;
34811                 default :
34812                     break;
34813             }
34814             
34815             Roo.each(box, function(b,kk){
34816                 
34817                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34818                 
34819                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34820                 
34821             }, this);
34822             
34823         }, this);
34824         
34825     },
34826     
34827     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34828     {
34829         Roo.each(eItems, function(b,k){
34830             
34831             b.size = (k == 0) ? 'sm' : 'xs';
34832             b.x = (k == 0) ? 2 : 1;
34833             b.y = (k == 0) ? 2 : 1;
34834             
34835             b.el.position('absolute');
34836             
34837             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34838                 
34839             b.el.setWidth(width);
34840             
34841             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34842             
34843             b.el.setHeight(height);
34844             
34845         }, this);
34846
34847         var positions = [];
34848         
34849         positions.push({
34850             x : maxX - this.unitWidth * 2 - this.gutter,
34851             y : minY
34852         });
34853         
34854         positions.push({
34855             x : maxX - this.unitWidth,
34856             y : minY + (this.unitWidth + this.gutter) * 2
34857         });
34858         
34859         positions.push({
34860             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34861             y : minY
34862         });
34863         
34864         Roo.each(eItems, function(b,k){
34865             
34866             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34867
34868         }, this);
34869         
34870     },
34871     
34872     getVerticalOneBoxColPositions : function(x, y, box)
34873     {
34874         var pos = [];
34875         
34876         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34877         
34878         if(box[0].size == 'md-left'){
34879             rand = 0;
34880         }
34881         
34882         if(box[0].size == 'md-right'){
34883             rand = 1;
34884         }
34885         
34886         pos.push({
34887             x : x + (this.unitWidth + this.gutter) * rand,
34888             y : y
34889         });
34890         
34891         return pos;
34892     },
34893     
34894     getVerticalTwoBoxColPositions : function(x, y, box)
34895     {
34896         var pos = [];
34897         
34898         if(box[0].size == 'xs'){
34899             
34900             pos.push({
34901                 x : x,
34902                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34903             });
34904
34905             pos.push({
34906                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34907                 y : y
34908             });
34909             
34910             return pos;
34911             
34912         }
34913         
34914         pos.push({
34915             x : x,
34916             y : y
34917         });
34918
34919         pos.push({
34920             x : x + (this.unitWidth + this.gutter) * 2,
34921             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34922         });
34923         
34924         return pos;
34925         
34926     },
34927     
34928     getVerticalThreeBoxColPositions : function(x, y, box)
34929     {
34930         var pos = [];
34931         
34932         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34933             
34934             pos.push({
34935                 x : x,
34936                 y : y
34937             });
34938
34939             pos.push({
34940                 x : x + (this.unitWidth + this.gutter) * 1,
34941                 y : y
34942             });
34943             
34944             pos.push({
34945                 x : x + (this.unitWidth + this.gutter) * 2,
34946                 y : y
34947             });
34948             
34949             return pos;
34950             
34951         }
34952         
34953         if(box[0].size == 'xs' && box[1].size == 'xs'){
34954             
34955             pos.push({
34956                 x : x,
34957                 y : y
34958             });
34959
34960             pos.push({
34961                 x : x,
34962                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34963             });
34964             
34965             pos.push({
34966                 x : x + (this.unitWidth + this.gutter) * 1,
34967                 y : y
34968             });
34969             
34970             return pos;
34971             
34972         }
34973         
34974         pos.push({
34975             x : x,
34976             y : y
34977         });
34978
34979         pos.push({
34980             x : x + (this.unitWidth + this.gutter) * 2,
34981             y : y
34982         });
34983
34984         pos.push({
34985             x : x + (this.unitWidth + this.gutter) * 2,
34986             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34987         });
34988             
34989         return pos;
34990         
34991     },
34992     
34993     getVerticalFourBoxColPositions : function(x, y, box)
34994     {
34995         var pos = [];
34996         
34997         if(box[0].size == 'xs'){
34998             
34999             pos.push({
35000                 x : x,
35001                 y : y
35002             });
35003
35004             pos.push({
35005                 x : x,
35006                 y : y + (this.unitHeight + this.gutter) * 1
35007             });
35008             
35009             pos.push({
35010                 x : x,
35011                 y : y + (this.unitHeight + this.gutter) * 2
35012             });
35013             
35014             pos.push({
35015                 x : x + (this.unitWidth + this.gutter) * 1,
35016                 y : y
35017             });
35018             
35019             return pos;
35020             
35021         }
35022         
35023         pos.push({
35024             x : x,
35025             y : y
35026         });
35027
35028         pos.push({
35029             x : x + (this.unitWidth + this.gutter) * 2,
35030             y : y
35031         });
35032
35033         pos.push({
35034             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35035             y : y + (this.unitHeight + this.gutter) * 1
35036         });
35037
35038         pos.push({
35039             x : x + (this.unitWidth + this.gutter) * 2,
35040             y : y + (this.unitWidth + this.gutter) * 2
35041         });
35042
35043         return pos;
35044         
35045     },
35046     
35047     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35048     {
35049         var pos = [];
35050         
35051         if(box[0].size == 'md-left'){
35052             pos.push({
35053                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35054                 y : minY
35055             });
35056             
35057             return pos;
35058         }
35059         
35060         if(box[0].size == 'md-right'){
35061             pos.push({
35062                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35063                 y : minY + (this.unitWidth + this.gutter) * 1
35064             });
35065             
35066             return pos;
35067         }
35068         
35069         var rand = Math.floor(Math.random() * (4 - box[0].y));
35070         
35071         pos.push({
35072             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35073             y : minY + (this.unitWidth + this.gutter) * rand
35074         });
35075         
35076         return pos;
35077         
35078     },
35079     
35080     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35081     {
35082         var pos = [];
35083         
35084         if(box[0].size == 'xs'){
35085             
35086             pos.push({
35087                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35088                 y : minY
35089             });
35090
35091             pos.push({
35092                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35093                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35094             });
35095             
35096             return pos;
35097             
35098         }
35099         
35100         pos.push({
35101             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35102             y : minY
35103         });
35104
35105         pos.push({
35106             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35107             y : minY + (this.unitWidth + this.gutter) * 2
35108         });
35109         
35110         return pos;
35111         
35112     },
35113     
35114     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35115     {
35116         var pos = [];
35117         
35118         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35119             
35120             pos.push({
35121                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35122                 y : minY
35123             });
35124
35125             pos.push({
35126                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35127                 y : minY + (this.unitWidth + this.gutter) * 1
35128             });
35129             
35130             pos.push({
35131                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35132                 y : minY + (this.unitWidth + this.gutter) * 2
35133             });
35134             
35135             return pos;
35136             
35137         }
35138         
35139         if(box[0].size == 'xs' && box[1].size == 'xs'){
35140             
35141             pos.push({
35142                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35143                 y : minY
35144             });
35145
35146             pos.push({
35147                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35148                 y : minY
35149             });
35150             
35151             pos.push({
35152                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35153                 y : minY + (this.unitWidth + this.gutter) * 1
35154             });
35155             
35156             return pos;
35157             
35158         }
35159         
35160         pos.push({
35161             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35162             y : minY
35163         });
35164
35165         pos.push({
35166             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35167             y : minY + (this.unitWidth + this.gutter) * 2
35168         });
35169
35170         pos.push({
35171             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35172             y : minY + (this.unitWidth + this.gutter) * 2
35173         });
35174             
35175         return pos;
35176         
35177     },
35178     
35179     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35180     {
35181         var pos = [];
35182         
35183         if(box[0].size == 'xs'){
35184             
35185             pos.push({
35186                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35187                 y : minY
35188             });
35189
35190             pos.push({
35191                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35197                 y : minY
35198             });
35199             
35200             pos.push({
35201                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35202                 y : minY + (this.unitWidth + this.gutter) * 1
35203             });
35204             
35205             return pos;
35206             
35207         }
35208         
35209         pos.push({
35210             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35211             y : minY
35212         });
35213         
35214         pos.push({
35215             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35216             y : minY + (this.unitWidth + this.gutter) * 2
35217         });
35218         
35219         pos.push({
35220             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35226             y : minY + (this.unitWidth + this.gutter) * 2
35227         });
35228
35229         return pos;
35230         
35231     },
35232     
35233     /**
35234     * remove a Masonry Brick
35235     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35236     */
35237     removeBrick : function(brick_id)
35238     {
35239         if (!brick_id) {
35240             return;
35241         }
35242         
35243         for (var i = 0; i<this.bricks.length; i++) {
35244             if (this.bricks[i].id == brick_id) {
35245                 this.bricks.splice(i,1);
35246                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35247                 this.initial();
35248             }
35249         }
35250     },
35251     
35252     /**
35253     * adds a Masonry Brick
35254     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35255     */
35256     addBrick : function(cfg)
35257     {
35258         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35259         //this.register(cn);
35260         cn.parentId = this.id;
35261         cn.render(this.el);
35262         return cn;
35263     },
35264     
35265     /**
35266     * register a Masonry Brick
35267     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35268     */
35269     
35270     register : function(brick)
35271     {
35272         this.bricks.push(brick);
35273         brick.masonryId = this.id;
35274     },
35275     
35276     /**
35277     * clear all the Masonry Brick
35278     */
35279     clearAll : function()
35280     {
35281         this.bricks = [];
35282         //this.getChildContainer().dom.innerHTML = "";
35283         this.el.dom.innerHTML = '';
35284     },
35285     
35286     getSelected : function()
35287     {
35288         if (!this.selectedBrick) {
35289             return false;
35290         }
35291         
35292         return this.selectedBrick;
35293     }
35294 });
35295
35296 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35297     
35298     groups: {},
35299      /**
35300     * register a Masonry Layout
35301     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35302     */
35303     
35304     register : function(layout)
35305     {
35306         this.groups[layout.id] = layout;
35307     },
35308     /**
35309     * fetch a  Masonry Layout based on the masonry layout ID
35310     * @param {string} the masonry layout to add
35311     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35312     */
35313     
35314     get: function(layout_id) {
35315         if (typeof(this.groups[layout_id]) == 'undefined') {
35316             return false;
35317         }
35318         return this.groups[layout_id] ;
35319     }
35320     
35321     
35322     
35323 });
35324
35325  
35326
35327  /**
35328  *
35329  * This is based on 
35330  * http://masonry.desandro.com
35331  *
35332  * The idea is to render all the bricks based on vertical width...
35333  *
35334  * The original code extends 'outlayer' - we might need to use that....
35335  * 
35336  */
35337
35338
35339 /**
35340  * @class Roo.bootstrap.LayoutMasonryAuto
35341  * @extends Roo.bootstrap.Component
35342  * Bootstrap Layout Masonry class
35343  * 
35344  * @constructor
35345  * Create a new Element
35346  * @param {Object} config The config object
35347  */
35348
35349 Roo.bootstrap.LayoutMasonryAuto = function(config){
35350     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35351 };
35352
35353 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35354     
35355       /**
35356      * @cfg {Boolean} isFitWidth  - resize the width..
35357      */   
35358     isFitWidth : false,  // options..
35359     /**
35360      * @cfg {Boolean} isOriginLeft = left align?
35361      */   
35362     isOriginLeft : true,
35363     /**
35364      * @cfg {Boolean} isOriginTop = top align?
35365      */   
35366     isOriginTop : false,
35367     /**
35368      * @cfg {Boolean} isLayoutInstant = no animation?
35369      */   
35370     isLayoutInstant : false, // needed?
35371     /**
35372      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35373      */   
35374     isResizingContainer : true,
35375     /**
35376      * @cfg {Number} columnWidth  width of the columns 
35377      */   
35378     
35379     columnWidth : 0,
35380     
35381     /**
35382      * @cfg {Number} maxCols maximum number of columns
35383      */   
35384     
35385     maxCols: 0,
35386     /**
35387      * @cfg {Number} padHeight padding below box..
35388      */   
35389     
35390     padHeight : 10, 
35391     
35392     /**
35393      * @cfg {Boolean} isAutoInitial defalut true
35394      */   
35395     
35396     isAutoInitial : true, 
35397     
35398     // private?
35399     gutter : 0,
35400     
35401     containerWidth: 0,
35402     initialColumnWidth : 0,
35403     currentSize : null,
35404     
35405     colYs : null, // array.
35406     maxY : 0,
35407     padWidth: 10,
35408     
35409     
35410     tag: 'div',
35411     cls: '',
35412     bricks: null, //CompositeElement
35413     cols : 0, // array?
35414     // element : null, // wrapped now this.el
35415     _isLayoutInited : null, 
35416     
35417     
35418     getAutoCreate : function(){
35419         
35420         var cfg = {
35421             tag: this.tag,
35422             cls: 'blog-masonary-wrapper ' + this.cls,
35423             cn : {
35424                 cls : 'mas-boxes masonary'
35425             }
35426         };
35427         
35428         return cfg;
35429     },
35430     
35431     getChildContainer: function( )
35432     {
35433         if (this.boxesEl) {
35434             return this.boxesEl;
35435         }
35436         
35437         this.boxesEl = this.el.select('.mas-boxes').first();
35438         
35439         return this.boxesEl;
35440     },
35441     
35442     
35443     initEvents : function()
35444     {
35445         var _this = this;
35446         
35447         if(this.isAutoInitial){
35448             Roo.log('hook children rendered');
35449             this.on('childrenrendered', function() {
35450                 Roo.log('children rendered');
35451                 _this.initial();
35452             } ,this);
35453         }
35454         
35455     },
35456     
35457     initial : function()
35458     {
35459         this.reloadItems();
35460
35461         this.currentSize = this.el.getBox(true);
35462
35463         /// was window resize... - let's see if this works..
35464         Roo.EventManager.onWindowResize(this.resize, this); 
35465
35466         if(!this.isAutoInitial){
35467             this.layout();
35468             return;
35469         }
35470         
35471         this.layout.defer(500,this);
35472     },
35473     
35474     reloadItems: function()
35475     {
35476         this.bricks = this.el.select('.masonry-brick', true);
35477         
35478         this.bricks.each(function(b) {
35479             //Roo.log(b.getSize());
35480             if (!b.attr('originalwidth')) {
35481                 b.attr('originalwidth',  b.getSize().width);
35482             }
35483             
35484         });
35485         
35486         Roo.log(this.bricks.elements.length);
35487     },
35488     
35489     resize : function()
35490     {
35491         Roo.log('resize');
35492         var cs = this.el.getBox(true);
35493         
35494         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35495             Roo.log("no change in with or X");
35496             return;
35497         }
35498         this.currentSize = cs;
35499         this.layout();
35500     },
35501     
35502     layout : function()
35503     {
35504          Roo.log('layout');
35505         this._resetLayout();
35506         //this._manageStamps();
35507       
35508         // don't animate first layout
35509         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35510         this.layoutItems( isInstant );
35511       
35512         // flag for initalized
35513         this._isLayoutInited = true;
35514     },
35515     
35516     layoutItems : function( isInstant )
35517     {
35518         //var items = this._getItemsForLayout( this.items );
35519         // original code supports filtering layout items.. we just ignore it..
35520         
35521         this._layoutItems( this.bricks , isInstant );
35522       
35523         this._postLayout();
35524     },
35525     _layoutItems : function ( items , isInstant)
35526     {
35527        //this.fireEvent( 'layout', this, items );
35528     
35529
35530         if ( !items || !items.elements.length ) {
35531           // no items, emit event with empty array
35532             return;
35533         }
35534
35535         var queue = [];
35536         items.each(function(item) {
35537             Roo.log("layout item");
35538             Roo.log(item);
35539             // get x/y object from method
35540             var position = this._getItemLayoutPosition( item );
35541             // enqueue
35542             position.item = item;
35543             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35544             queue.push( position );
35545         }, this);
35546       
35547         this._processLayoutQueue( queue );
35548     },
35549     /** Sets position of item in DOM
35550     * @param {Element} item
35551     * @param {Number} x - horizontal position
35552     * @param {Number} y - vertical position
35553     * @param {Boolean} isInstant - disables transitions
35554     */
35555     _processLayoutQueue : function( queue )
35556     {
35557         for ( var i=0, len = queue.length; i < len; i++ ) {
35558             var obj = queue[i];
35559             obj.item.position('absolute');
35560             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35561         }
35562     },
35563       
35564     
35565     /**
35566     * Any logic you want to do after each layout,
35567     * i.e. size the container
35568     */
35569     _postLayout : function()
35570     {
35571         this.resizeContainer();
35572     },
35573     
35574     resizeContainer : function()
35575     {
35576         if ( !this.isResizingContainer ) {
35577             return;
35578         }
35579         var size = this._getContainerSize();
35580         if ( size ) {
35581             this.el.setSize(size.width,size.height);
35582             this.boxesEl.setSize(size.width,size.height);
35583         }
35584     },
35585     
35586     
35587     
35588     _resetLayout : function()
35589     {
35590         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35591         this.colWidth = this.el.getWidth();
35592         //this.gutter = this.el.getWidth(); 
35593         
35594         this.measureColumns();
35595
35596         // reset column Y
35597         var i = this.cols;
35598         this.colYs = [];
35599         while (i--) {
35600             this.colYs.push( 0 );
35601         }
35602     
35603         this.maxY = 0;
35604     },
35605
35606     measureColumns : function()
35607     {
35608         this.getContainerWidth();
35609       // if columnWidth is 0, default to outerWidth of first item
35610         if ( !this.columnWidth ) {
35611             var firstItem = this.bricks.first();
35612             Roo.log(firstItem);
35613             this.columnWidth  = this.containerWidth;
35614             if (firstItem && firstItem.attr('originalwidth') ) {
35615                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35616             }
35617             // columnWidth fall back to item of first element
35618             Roo.log("set column width?");
35619                         this.initialColumnWidth = this.columnWidth  ;
35620
35621             // if first elem has no width, default to size of container
35622             
35623         }
35624         
35625         
35626         if (this.initialColumnWidth) {
35627             this.columnWidth = this.initialColumnWidth;
35628         }
35629         
35630         
35631             
35632         // column width is fixed at the top - however if container width get's smaller we should
35633         // reduce it...
35634         
35635         // this bit calcs how man columns..
35636             
35637         var columnWidth = this.columnWidth += this.gutter;
35638       
35639         // calculate columns
35640         var containerWidth = this.containerWidth + this.gutter;
35641         
35642         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35643         // fix rounding errors, typically with gutters
35644         var excess = columnWidth - containerWidth % columnWidth;
35645         
35646         
35647         // if overshoot is less than a pixel, round up, otherwise floor it
35648         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35649         cols = Math[ mathMethod ]( cols );
35650         this.cols = Math.max( cols, 1 );
35651         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35652         
35653          // padding positioning..
35654         var totalColWidth = this.cols * this.columnWidth;
35655         var padavail = this.containerWidth - totalColWidth;
35656         // so for 2 columns - we need 3 'pads'
35657         
35658         var padNeeded = (1+this.cols) * this.padWidth;
35659         
35660         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35661         
35662         this.columnWidth += padExtra
35663         //this.padWidth = Math.floor(padavail /  ( this.cols));
35664         
35665         // adjust colum width so that padding is fixed??
35666         
35667         // we have 3 columns ... total = width * 3
35668         // we have X left over... that should be used by 
35669         
35670         //if (this.expandC) {
35671             
35672         //}
35673         
35674         
35675         
35676     },
35677     
35678     getContainerWidth : function()
35679     {
35680        /* // container is parent if fit width
35681         var container = this.isFitWidth ? this.element.parentNode : this.element;
35682         // check that this.size and size are there
35683         // IE8 triggers resize on body size change, so they might not be
35684         
35685         var size = getSize( container );  //FIXME
35686         this.containerWidth = size && size.innerWidth; //FIXME
35687         */
35688          
35689         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35690         
35691     },
35692     
35693     _getItemLayoutPosition : function( item )  // what is item?
35694     {
35695         // we resize the item to our columnWidth..
35696       
35697         item.setWidth(this.columnWidth);
35698         item.autoBoxAdjust  = false;
35699         
35700         var sz = item.getSize();
35701  
35702         // how many columns does this brick span
35703         var remainder = this.containerWidth % this.columnWidth;
35704         
35705         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35706         // round if off by 1 pixel, otherwise use ceil
35707         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35708         colSpan = Math.min( colSpan, this.cols );
35709         
35710         // normally this should be '1' as we dont' currently allow multi width columns..
35711         
35712         var colGroup = this._getColGroup( colSpan );
35713         // get the minimum Y value from the columns
35714         var minimumY = Math.min.apply( Math, colGroup );
35715         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35716         
35717         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35718          
35719         // position the brick
35720         var position = {
35721             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35722             y: this.currentSize.y + minimumY + this.padHeight
35723         };
35724         
35725         Roo.log(position);
35726         // apply setHeight to necessary columns
35727         var setHeight = minimumY + sz.height + this.padHeight;
35728         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35729         
35730         var setSpan = this.cols + 1 - colGroup.length;
35731         for ( var i = 0; i < setSpan; i++ ) {
35732           this.colYs[ shortColIndex + i ] = setHeight ;
35733         }
35734       
35735         return position;
35736     },
35737     
35738     /**
35739      * @param {Number} colSpan - number of columns the element spans
35740      * @returns {Array} colGroup
35741      */
35742     _getColGroup : function( colSpan )
35743     {
35744         if ( colSpan < 2 ) {
35745           // if brick spans only one column, use all the column Ys
35746           return this.colYs;
35747         }
35748       
35749         var colGroup = [];
35750         // how many different places could this brick fit horizontally
35751         var groupCount = this.cols + 1 - colSpan;
35752         // for each group potential horizontal position
35753         for ( var i = 0; i < groupCount; i++ ) {
35754           // make an array of colY values for that one group
35755           var groupColYs = this.colYs.slice( i, i + colSpan );
35756           // and get the max value of the array
35757           colGroup[i] = Math.max.apply( Math, groupColYs );
35758         }
35759         return colGroup;
35760     },
35761     /*
35762     _manageStamp : function( stamp )
35763     {
35764         var stampSize =  stamp.getSize();
35765         var offset = stamp.getBox();
35766         // get the columns that this stamp affects
35767         var firstX = this.isOriginLeft ? offset.x : offset.right;
35768         var lastX = firstX + stampSize.width;
35769         var firstCol = Math.floor( firstX / this.columnWidth );
35770         firstCol = Math.max( 0, firstCol );
35771         
35772         var lastCol = Math.floor( lastX / this.columnWidth );
35773         // lastCol should not go over if multiple of columnWidth #425
35774         lastCol -= lastX % this.columnWidth ? 0 : 1;
35775         lastCol = Math.min( this.cols - 1, lastCol );
35776         
35777         // set colYs to bottom of the stamp
35778         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35779             stampSize.height;
35780             
35781         for ( var i = firstCol; i <= lastCol; i++ ) {
35782           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35783         }
35784     },
35785     */
35786     
35787     _getContainerSize : function()
35788     {
35789         this.maxY = Math.max.apply( Math, this.colYs );
35790         var size = {
35791             height: this.maxY
35792         };
35793       
35794         if ( this.isFitWidth ) {
35795             size.width = this._getContainerFitWidth();
35796         }
35797       
35798         return size;
35799     },
35800     
35801     _getContainerFitWidth : function()
35802     {
35803         var unusedCols = 0;
35804         // count unused columns
35805         var i = this.cols;
35806         while ( --i ) {
35807           if ( this.colYs[i] !== 0 ) {
35808             break;
35809           }
35810           unusedCols++;
35811         }
35812         // fit container to columns that have been used
35813         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35814     },
35815     
35816     needsResizeLayout : function()
35817     {
35818         var previousWidth = this.containerWidth;
35819         this.getContainerWidth();
35820         return previousWidth !== this.containerWidth;
35821     }
35822  
35823 });
35824
35825  
35826
35827  /*
35828  * - LGPL
35829  *
35830  * element
35831  * 
35832  */
35833
35834 /**
35835  * @class Roo.bootstrap.MasonryBrick
35836  * @extends Roo.bootstrap.Component
35837  * Bootstrap MasonryBrick class
35838  * 
35839  * @constructor
35840  * Create a new MasonryBrick
35841  * @param {Object} config The config object
35842  */
35843
35844 Roo.bootstrap.MasonryBrick = function(config){
35845     
35846     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35847     
35848     Roo.bootstrap.MasonryBrick.register(this);
35849     
35850     this.addEvents({
35851         // raw events
35852         /**
35853          * @event click
35854          * When a MasonryBrick is clcik
35855          * @param {Roo.bootstrap.MasonryBrick} this
35856          * @param {Roo.EventObject} e
35857          */
35858         "click" : true
35859     });
35860 };
35861
35862 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35863     
35864     /**
35865      * @cfg {String} title
35866      */   
35867     title : '',
35868     /**
35869      * @cfg {String} html
35870      */   
35871     html : '',
35872     /**
35873      * @cfg {String} bgimage
35874      */   
35875     bgimage : '',
35876     /**
35877      * @cfg {String} videourl
35878      */   
35879     videourl : '',
35880     /**
35881      * @cfg {String} cls
35882      */   
35883     cls : '',
35884     /**
35885      * @cfg {String} href
35886      */   
35887     href : '',
35888     /**
35889      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35890      */   
35891     size : 'xs',
35892     
35893     /**
35894      * @cfg {String} placetitle (center|bottom)
35895      */   
35896     placetitle : '',
35897     
35898     /**
35899      * @cfg {Boolean} isFitContainer defalut true
35900      */   
35901     isFitContainer : true, 
35902     
35903     /**
35904      * @cfg {Boolean} preventDefault defalut false
35905      */   
35906     preventDefault : false, 
35907     
35908     /**
35909      * @cfg {Boolean} inverse defalut false
35910      */   
35911     maskInverse : false, 
35912     
35913     getAutoCreate : function()
35914     {
35915         if(!this.isFitContainer){
35916             return this.getSplitAutoCreate();
35917         }
35918         
35919         var cls = 'masonry-brick masonry-brick-full';
35920         
35921         if(this.href.length){
35922             cls += ' masonry-brick-link';
35923         }
35924         
35925         if(this.bgimage.length){
35926             cls += ' masonry-brick-image';
35927         }
35928         
35929         if(this.maskInverse){
35930             cls += ' mask-inverse';
35931         }
35932         
35933         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35934             cls += ' enable-mask';
35935         }
35936         
35937         if(this.size){
35938             cls += ' masonry-' + this.size + '-brick';
35939         }
35940         
35941         if(this.placetitle.length){
35942             
35943             switch (this.placetitle) {
35944                 case 'center' :
35945                     cls += ' masonry-center-title';
35946                     break;
35947                 case 'bottom' :
35948                     cls += ' masonry-bottom-title';
35949                     break;
35950                 default:
35951                     break;
35952             }
35953             
35954         } else {
35955             if(!this.html.length && !this.bgimage.length){
35956                 cls += ' masonry-center-title';
35957             }
35958
35959             if(!this.html.length && this.bgimage.length){
35960                 cls += ' masonry-bottom-title';
35961             }
35962         }
35963         
35964         if(this.cls){
35965             cls += ' ' + this.cls;
35966         }
35967         
35968         var cfg = {
35969             tag: (this.href.length) ? 'a' : 'div',
35970             cls: cls,
35971             cn: [
35972                 {
35973                     tag: 'div',
35974                     cls: 'masonry-brick-mask'
35975                 },
35976                 {
35977                     tag: 'div',
35978                     cls: 'masonry-brick-paragraph',
35979                     cn: []
35980                 }
35981             ]
35982         };
35983         
35984         if(this.href.length){
35985             cfg.href = this.href;
35986         }
35987         
35988         var cn = cfg.cn[1].cn;
35989         
35990         if(this.title.length){
35991             cn.push({
35992                 tag: 'h4',
35993                 cls: 'masonry-brick-title',
35994                 html: this.title
35995             });
35996         }
35997         
35998         if(this.html.length){
35999             cn.push({
36000                 tag: 'p',
36001                 cls: 'masonry-brick-text',
36002                 html: this.html
36003             });
36004         }
36005         
36006         if (!this.title.length && !this.html.length) {
36007             cfg.cn[1].cls += ' hide';
36008         }
36009         
36010         if(this.bgimage.length){
36011             cfg.cn.push({
36012                 tag: 'img',
36013                 cls: 'masonry-brick-image-view',
36014                 src: this.bgimage
36015             });
36016         }
36017         
36018         if(this.videourl.length){
36019             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36020             // youtube support only?
36021             cfg.cn.push({
36022                 tag: 'iframe',
36023                 cls: 'masonry-brick-image-view',
36024                 src: vurl,
36025                 frameborder : 0,
36026                 allowfullscreen : true
36027             });
36028         }
36029         
36030         return cfg;
36031         
36032     },
36033     
36034     getSplitAutoCreate : function()
36035     {
36036         var cls = 'masonry-brick masonry-brick-split';
36037         
36038         if(this.href.length){
36039             cls += ' masonry-brick-link';
36040         }
36041         
36042         if(this.bgimage.length){
36043             cls += ' masonry-brick-image';
36044         }
36045         
36046         if(this.size){
36047             cls += ' masonry-' + this.size + '-brick';
36048         }
36049         
36050         switch (this.placetitle) {
36051             case 'center' :
36052                 cls += ' masonry-center-title';
36053                 break;
36054             case 'bottom' :
36055                 cls += ' masonry-bottom-title';
36056                 break;
36057             default:
36058                 if(!this.bgimage.length){
36059                     cls += ' masonry-center-title';
36060                 }
36061
36062                 if(this.bgimage.length){
36063                     cls += ' masonry-bottom-title';
36064                 }
36065                 break;
36066         }
36067         
36068         if(this.cls){
36069             cls += ' ' + this.cls;
36070         }
36071         
36072         var cfg = {
36073             tag: (this.href.length) ? 'a' : 'div',
36074             cls: cls,
36075             cn: [
36076                 {
36077                     tag: 'div',
36078                     cls: 'masonry-brick-split-head',
36079                     cn: [
36080                         {
36081                             tag: 'div',
36082                             cls: 'masonry-brick-paragraph',
36083                             cn: []
36084                         }
36085                     ]
36086                 },
36087                 {
36088                     tag: 'div',
36089                     cls: 'masonry-brick-split-body',
36090                     cn: []
36091                 }
36092             ]
36093         };
36094         
36095         if(this.href.length){
36096             cfg.href = this.href;
36097         }
36098         
36099         if(this.title.length){
36100             cfg.cn[0].cn[0].cn.push({
36101                 tag: 'h4',
36102                 cls: 'masonry-brick-title',
36103                 html: this.title
36104             });
36105         }
36106         
36107         if(this.html.length){
36108             cfg.cn[1].cn.push({
36109                 tag: 'p',
36110                 cls: 'masonry-brick-text',
36111                 html: this.html
36112             });
36113         }
36114
36115         if(this.bgimage.length){
36116             cfg.cn[0].cn.push({
36117                 tag: 'img',
36118                 cls: 'masonry-brick-image-view',
36119                 src: this.bgimage
36120             });
36121         }
36122         
36123         if(this.videourl.length){
36124             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36125             // youtube support only?
36126             cfg.cn[0].cn.cn.push({
36127                 tag: 'iframe',
36128                 cls: 'masonry-brick-image-view',
36129                 src: vurl,
36130                 frameborder : 0,
36131                 allowfullscreen : true
36132             });
36133         }
36134         
36135         return cfg;
36136     },
36137     
36138     initEvents: function() 
36139     {
36140         switch (this.size) {
36141             case 'xs' :
36142                 this.x = 1;
36143                 this.y = 1;
36144                 break;
36145             case 'sm' :
36146                 this.x = 2;
36147                 this.y = 2;
36148                 break;
36149             case 'md' :
36150             case 'md-left' :
36151             case 'md-right' :
36152                 this.x = 3;
36153                 this.y = 3;
36154                 break;
36155             case 'tall' :
36156                 this.x = 2;
36157                 this.y = 3;
36158                 break;
36159             case 'wide' :
36160                 this.x = 3;
36161                 this.y = 2;
36162                 break;
36163             case 'wide-thin' :
36164                 this.x = 3;
36165                 this.y = 1;
36166                 break;
36167                         
36168             default :
36169                 break;
36170         }
36171         
36172         if(Roo.isTouch){
36173             this.el.on('touchstart', this.onTouchStart, this);
36174             this.el.on('touchmove', this.onTouchMove, this);
36175             this.el.on('touchend', this.onTouchEnd, this);
36176             this.el.on('contextmenu', this.onContextMenu, this);
36177         } else {
36178             this.el.on('mouseenter'  ,this.enter, this);
36179             this.el.on('mouseleave', this.leave, this);
36180             this.el.on('click', this.onClick, this);
36181         }
36182         
36183         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36184             this.parent().bricks.push(this);   
36185         }
36186         
36187     },
36188     
36189     onClick: function(e, el)
36190     {
36191         var time = this.endTimer - this.startTimer;
36192         // Roo.log(e.preventDefault());
36193         if(Roo.isTouch){
36194             if(time > 1000){
36195                 e.preventDefault();
36196                 return;
36197             }
36198         }
36199         
36200         if(!this.preventDefault){
36201             return;
36202         }
36203         
36204         e.preventDefault();
36205         
36206         if (this.activeClass != '') {
36207             this.selectBrick();
36208         }
36209         
36210         this.fireEvent('click', this, e);
36211     },
36212     
36213     enter: function(e, el)
36214     {
36215         e.preventDefault();
36216         
36217         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36218             return;
36219         }
36220         
36221         if(this.bgimage.length && this.html.length){
36222             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36223         }
36224     },
36225     
36226     leave: function(e, el)
36227     {
36228         e.preventDefault();
36229         
36230         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36231             return;
36232         }
36233         
36234         if(this.bgimage.length && this.html.length){
36235             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36236         }
36237     },
36238     
36239     onTouchStart: function(e, el)
36240     {
36241 //        e.preventDefault();
36242         
36243         this.touchmoved = false;
36244         
36245         if(!this.isFitContainer){
36246             return;
36247         }
36248         
36249         if(!this.bgimage.length || !this.html.length){
36250             return;
36251         }
36252         
36253         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36254         
36255         this.timer = new Date().getTime();
36256         
36257     },
36258     
36259     onTouchMove: function(e, el)
36260     {
36261         this.touchmoved = true;
36262     },
36263     
36264     onContextMenu : function(e,el)
36265     {
36266         e.preventDefault();
36267         e.stopPropagation();
36268         return false;
36269     },
36270     
36271     onTouchEnd: function(e, el)
36272     {
36273 //        e.preventDefault();
36274         
36275         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36276         
36277             this.leave(e,el);
36278             
36279             return;
36280         }
36281         
36282         if(!this.bgimage.length || !this.html.length){
36283             
36284             if(this.href.length){
36285                 window.location.href = this.href;
36286             }
36287             
36288             return;
36289         }
36290         
36291         if(!this.isFitContainer){
36292             return;
36293         }
36294         
36295         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36296         
36297         window.location.href = this.href;
36298     },
36299     
36300     //selection on single brick only
36301     selectBrick : function() {
36302         
36303         if (!this.parentId) {
36304             return;
36305         }
36306         
36307         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36308         var index = m.selectedBrick.indexOf(this.id);
36309         
36310         if ( index > -1) {
36311             m.selectedBrick.splice(index,1);
36312             this.el.removeClass(this.activeClass);
36313             return;
36314         }
36315         
36316         for(var i = 0; i < m.selectedBrick.length; i++) {
36317             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36318             b.el.removeClass(b.activeClass);
36319         }
36320         
36321         m.selectedBrick = [];
36322         
36323         m.selectedBrick.push(this.id);
36324         this.el.addClass(this.activeClass);
36325         return;
36326     },
36327     
36328     isSelected : function(){
36329         return this.el.hasClass(this.activeClass);
36330         
36331     }
36332 });
36333
36334 Roo.apply(Roo.bootstrap.MasonryBrick, {
36335     
36336     //groups: {},
36337     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36338      /**
36339     * register a Masonry Brick
36340     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36341     */
36342     
36343     register : function(brick)
36344     {
36345         //this.groups[brick.id] = brick;
36346         this.groups.add(brick.id, brick);
36347     },
36348     /**
36349     * fetch a  masonry brick based on the masonry brick ID
36350     * @param {string} the masonry brick to add
36351     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36352     */
36353     
36354     get: function(brick_id) 
36355     {
36356         // if (typeof(this.groups[brick_id]) == 'undefined') {
36357         //     return false;
36358         // }
36359         // return this.groups[brick_id] ;
36360         
36361         if(this.groups.key(brick_id)) {
36362             return this.groups.key(brick_id);
36363         }
36364         
36365         return false;
36366     }
36367     
36368     
36369     
36370 });
36371
36372  /*
36373  * - LGPL
36374  *
36375  * element
36376  * 
36377  */
36378
36379 /**
36380  * @class Roo.bootstrap.Brick
36381  * @extends Roo.bootstrap.Component
36382  * Bootstrap Brick class
36383  * 
36384  * @constructor
36385  * Create a new Brick
36386  * @param {Object} config The config object
36387  */
36388
36389 Roo.bootstrap.Brick = function(config){
36390     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36391     
36392     this.addEvents({
36393         // raw events
36394         /**
36395          * @event click
36396          * When a Brick is click
36397          * @param {Roo.bootstrap.Brick} this
36398          * @param {Roo.EventObject} e
36399          */
36400         "click" : true
36401     });
36402 };
36403
36404 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36405     
36406     /**
36407      * @cfg {String} title
36408      */   
36409     title : '',
36410     /**
36411      * @cfg {String} html
36412      */   
36413     html : '',
36414     /**
36415      * @cfg {String} bgimage
36416      */   
36417     bgimage : '',
36418     /**
36419      * @cfg {String} cls
36420      */   
36421     cls : '',
36422     /**
36423      * @cfg {String} href
36424      */   
36425     href : '',
36426     /**
36427      * @cfg {String} video
36428      */   
36429     video : '',
36430     /**
36431      * @cfg {Boolean} square
36432      */   
36433     square : true,
36434     
36435     getAutoCreate : function()
36436     {
36437         var cls = 'roo-brick';
36438         
36439         if(this.href.length){
36440             cls += ' roo-brick-link';
36441         }
36442         
36443         if(this.bgimage.length){
36444             cls += ' roo-brick-image';
36445         }
36446         
36447         if(!this.html.length && !this.bgimage.length){
36448             cls += ' roo-brick-center-title';
36449         }
36450         
36451         if(!this.html.length && this.bgimage.length){
36452             cls += ' roo-brick-bottom-title';
36453         }
36454         
36455         if(this.cls){
36456             cls += ' ' + this.cls;
36457         }
36458         
36459         var cfg = {
36460             tag: (this.href.length) ? 'a' : 'div',
36461             cls: cls,
36462             cn: [
36463                 {
36464                     tag: 'div',
36465                     cls: 'roo-brick-paragraph',
36466                     cn: []
36467                 }
36468             ]
36469         };
36470         
36471         if(this.href.length){
36472             cfg.href = this.href;
36473         }
36474         
36475         var cn = cfg.cn[0].cn;
36476         
36477         if(this.title.length){
36478             cn.push({
36479                 tag: 'h4',
36480                 cls: 'roo-brick-title',
36481                 html: this.title
36482             });
36483         }
36484         
36485         if(this.html.length){
36486             cn.push({
36487                 tag: 'p',
36488                 cls: 'roo-brick-text',
36489                 html: this.html
36490             });
36491         } else {
36492             cn.cls += ' hide';
36493         }
36494         
36495         if(this.bgimage.length){
36496             cfg.cn.push({
36497                 tag: 'img',
36498                 cls: 'roo-brick-image-view',
36499                 src: this.bgimage
36500             });
36501         }
36502         
36503         return cfg;
36504     },
36505     
36506     initEvents: function() 
36507     {
36508         if(this.title.length || this.html.length){
36509             this.el.on('mouseenter'  ,this.enter, this);
36510             this.el.on('mouseleave', this.leave, this);
36511         }
36512         
36513         Roo.EventManager.onWindowResize(this.resize, this); 
36514         
36515         if(this.bgimage.length){
36516             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36517             this.imageEl.on('load', this.onImageLoad, this);
36518             return;
36519         }
36520         
36521         this.resize();
36522     },
36523     
36524     onImageLoad : function()
36525     {
36526         this.resize();
36527     },
36528     
36529     resize : function()
36530     {
36531         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36532         
36533         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36534         
36535         if(this.bgimage.length){
36536             var image = this.el.select('.roo-brick-image-view', true).first();
36537             
36538             image.setWidth(paragraph.getWidth());
36539             
36540             if(this.square){
36541                 image.setHeight(paragraph.getWidth());
36542             }
36543             
36544             this.el.setHeight(image.getHeight());
36545             paragraph.setHeight(image.getHeight());
36546             
36547         }
36548         
36549     },
36550     
36551     enter: function(e, el)
36552     {
36553         e.preventDefault();
36554         
36555         if(this.bgimage.length){
36556             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36557             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36558         }
36559     },
36560     
36561     leave: function(e, el)
36562     {
36563         e.preventDefault();
36564         
36565         if(this.bgimage.length){
36566             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36567             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36568         }
36569     }
36570     
36571 });
36572
36573  
36574
36575  /*
36576  * - LGPL
36577  *
36578  * Number field 
36579  */
36580
36581 /**
36582  * @class Roo.bootstrap.NumberField
36583  * @extends Roo.bootstrap.Input
36584  * Bootstrap NumberField class
36585  * 
36586  * 
36587  * 
36588  * 
36589  * @constructor
36590  * Create a new NumberField
36591  * @param {Object} config The config object
36592  */
36593
36594 Roo.bootstrap.NumberField = function(config){
36595     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36596 };
36597
36598 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36599     
36600     /**
36601      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36602      */
36603     allowDecimals : true,
36604     /**
36605      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36606      */
36607     decimalSeparator : ".",
36608     /**
36609      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36610      */
36611     decimalPrecision : 2,
36612     /**
36613      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36614      */
36615     allowNegative : true,
36616     
36617     /**
36618      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36619      */
36620     allowZero: true,
36621     /**
36622      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36623      */
36624     minValue : Number.NEGATIVE_INFINITY,
36625     /**
36626      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36627      */
36628     maxValue : Number.MAX_VALUE,
36629     /**
36630      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36631      */
36632     minText : "The minimum value for this field is {0}",
36633     /**
36634      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36635      */
36636     maxText : "The maximum value for this field is {0}",
36637     /**
36638      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36639      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36640      */
36641     nanText : "{0} is not a valid number",
36642     /**
36643      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36644      */
36645     thousandsDelimiter : false,
36646     /**
36647      * @cfg {String} valueAlign alignment of value
36648      */
36649     valueAlign : "left",
36650
36651     getAutoCreate : function()
36652     {
36653         var hiddenInput = {
36654             tag: 'input',
36655             type: 'hidden',
36656             id: Roo.id(),
36657             cls: 'hidden-number-input'
36658         };
36659         
36660         if (this.name) {
36661             hiddenInput.name = this.name;
36662         }
36663         
36664         this.name = '';
36665         
36666         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36667         
36668         this.name = hiddenInput.name;
36669         
36670         if(cfg.cn.length > 0) {
36671             cfg.cn.push(hiddenInput);
36672         }
36673         
36674         return cfg;
36675     },
36676
36677     // private
36678     initEvents : function()
36679     {   
36680         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36681         
36682         var allowed = "0123456789";
36683         
36684         if(this.allowDecimals){
36685             allowed += this.decimalSeparator;
36686         }
36687         
36688         if(this.allowNegative){
36689             allowed += "-";
36690         }
36691         
36692         if(this.thousandsDelimiter) {
36693             allowed += ",";
36694         }
36695         
36696         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36697         
36698         var keyPress = function(e){
36699             
36700             var k = e.getKey();
36701             
36702             var c = e.getCharCode();
36703             
36704             if(
36705                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36706                     allowed.indexOf(String.fromCharCode(c)) === -1
36707             ){
36708                 e.stopEvent();
36709                 return;
36710             }
36711             
36712             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36713                 return;
36714             }
36715             
36716             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36717                 e.stopEvent();
36718             }
36719         };
36720         
36721         this.el.on("keypress", keyPress, this);
36722     },
36723     
36724     validateValue : function(value)
36725     {
36726         
36727         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36728             return false;
36729         }
36730         
36731         var num = this.parseValue(value);
36732         
36733         if(isNaN(num)){
36734             this.markInvalid(String.format(this.nanText, value));
36735             return false;
36736         }
36737         
36738         if(num < this.minValue){
36739             this.markInvalid(String.format(this.minText, this.minValue));
36740             return false;
36741         }
36742         
36743         if(num > this.maxValue){
36744             this.markInvalid(String.format(this.maxText, this.maxValue));
36745             return false;
36746         }
36747         
36748         return true;
36749     },
36750
36751     getValue : function()
36752     {
36753         var v = this.hiddenEl().getValue();
36754         
36755         return this.fixPrecision(this.parseValue(v));
36756     },
36757
36758     parseValue : function(value)
36759     {
36760         if(this.thousandsDelimiter) {
36761             value += "";
36762             r = new RegExp(",", "g");
36763             value = value.replace(r, "");
36764         }
36765         
36766         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36767         return isNaN(value) ? '' : value;
36768     },
36769
36770     fixPrecision : function(value)
36771     {
36772         if(this.thousandsDelimiter) {
36773             value += "";
36774             r = new RegExp(",", "g");
36775             value = value.replace(r, "");
36776         }
36777         
36778         var nan = isNaN(value);
36779         
36780         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36781             return nan ? '' : value;
36782         }
36783         return parseFloat(value).toFixed(this.decimalPrecision);
36784     },
36785
36786     setValue : function(v)
36787     {
36788         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36789         
36790         this.value = v;
36791         
36792         if(this.rendered){
36793             
36794             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36795             
36796             this.inputEl().dom.value = (v == '') ? '' :
36797                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36798             
36799             if(!this.allowZero && v === '0') {
36800                 this.hiddenEl().dom.value = '';
36801                 this.inputEl().dom.value = '';
36802             }
36803             
36804             this.validate();
36805         }
36806     },
36807
36808     decimalPrecisionFcn : function(v)
36809     {
36810         return Math.floor(v);
36811     },
36812
36813     beforeBlur : function()
36814     {
36815         var v = this.parseValue(this.getRawValue());
36816         
36817         if(v || v === 0 || v === ''){
36818             this.setValue(v);
36819         }
36820     },
36821     
36822     hiddenEl : function()
36823     {
36824         return this.el.select('input.hidden-number-input',true).first();
36825     }
36826     
36827 });
36828
36829  
36830
36831 /*
36832 * Licence: LGPL
36833 */
36834
36835 /**
36836  * @class Roo.bootstrap.DocumentSlider
36837  * @extends Roo.bootstrap.Component
36838  * Bootstrap DocumentSlider class
36839  * 
36840  * @constructor
36841  * Create a new DocumentViewer
36842  * @param {Object} config The config object
36843  */
36844
36845 Roo.bootstrap.DocumentSlider = function(config){
36846     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36847     
36848     this.files = [];
36849     
36850     this.addEvents({
36851         /**
36852          * @event initial
36853          * Fire after initEvent
36854          * @param {Roo.bootstrap.DocumentSlider} this
36855          */
36856         "initial" : true,
36857         /**
36858          * @event update
36859          * Fire after update
36860          * @param {Roo.bootstrap.DocumentSlider} this
36861          */
36862         "update" : true,
36863         /**
36864          * @event click
36865          * Fire after click
36866          * @param {Roo.bootstrap.DocumentSlider} this
36867          */
36868         "click" : true
36869     });
36870 };
36871
36872 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36873     
36874     files : false,
36875     
36876     indicator : 0,
36877     
36878     getAutoCreate : function()
36879     {
36880         var cfg = {
36881             tag : 'div',
36882             cls : 'roo-document-slider',
36883             cn : [
36884                 {
36885                     tag : 'div',
36886                     cls : 'roo-document-slider-header',
36887                     cn : [
36888                         {
36889                             tag : 'div',
36890                             cls : 'roo-document-slider-header-title'
36891                         }
36892                     ]
36893                 },
36894                 {
36895                     tag : 'div',
36896                     cls : 'roo-document-slider-body',
36897                     cn : [
36898                         {
36899                             tag : 'div',
36900                             cls : 'roo-document-slider-prev',
36901                             cn : [
36902                                 {
36903                                     tag : 'i',
36904                                     cls : 'fa fa-chevron-left'
36905                                 }
36906                             ]
36907                         },
36908                         {
36909                             tag : 'div',
36910                             cls : 'roo-document-slider-thumb',
36911                             cn : [
36912                                 {
36913                                     tag : 'img',
36914                                     cls : 'roo-document-slider-image'
36915                                 }
36916                             ]
36917                         },
36918                         {
36919                             tag : 'div',
36920                             cls : 'roo-document-slider-next',
36921                             cn : [
36922                                 {
36923                                     tag : 'i',
36924                                     cls : 'fa fa-chevron-right'
36925                                 }
36926                             ]
36927                         }
36928                     ]
36929                 }
36930             ]
36931         };
36932         
36933         return cfg;
36934     },
36935     
36936     initEvents : function()
36937     {
36938         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36939         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36940         
36941         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36942         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36943         
36944         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36945         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36946         
36947         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36948         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36949         
36950         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36951         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36952         
36953         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36954         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36955         
36956         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36957         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36958         
36959         this.thumbEl.on('click', this.onClick, this);
36960         
36961         this.prevIndicator.on('click', this.prev, this);
36962         
36963         this.nextIndicator.on('click', this.next, this);
36964         
36965     },
36966     
36967     initial : function()
36968     {
36969         if(this.files.length){
36970             this.indicator = 1;
36971             this.update()
36972         }
36973         
36974         this.fireEvent('initial', this);
36975     },
36976     
36977     update : function()
36978     {
36979         this.imageEl.attr('src', this.files[this.indicator - 1]);
36980         
36981         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36982         
36983         this.prevIndicator.show();
36984         
36985         if(this.indicator == 1){
36986             this.prevIndicator.hide();
36987         }
36988         
36989         this.nextIndicator.show();
36990         
36991         if(this.indicator == this.files.length){
36992             this.nextIndicator.hide();
36993         }
36994         
36995         this.thumbEl.scrollTo('top');
36996         
36997         this.fireEvent('update', this);
36998     },
36999     
37000     onClick : function(e)
37001     {
37002         e.preventDefault();
37003         
37004         this.fireEvent('click', this);
37005     },
37006     
37007     prev : function(e)
37008     {
37009         e.preventDefault();
37010         
37011         this.indicator = Math.max(1, this.indicator - 1);
37012         
37013         this.update();
37014     },
37015     
37016     next : function(e)
37017     {
37018         e.preventDefault();
37019         
37020         this.indicator = Math.min(this.files.length, this.indicator + 1);
37021         
37022         this.update();
37023     }
37024 });
37025 /*
37026  * - LGPL
37027  *
37028  * RadioSet
37029  *
37030  *
37031  */
37032
37033 /**
37034  * @class Roo.bootstrap.RadioSet
37035  * @extends Roo.bootstrap.Input
37036  * @children Roo.bootstrap.Radio
37037  * Bootstrap RadioSet class
37038  * @cfg {String} indicatorpos (left|right) default left
37039  * @cfg {Boolean} inline (true|false) inline the element (default true)
37040  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37041  * @constructor
37042  * Create a new RadioSet
37043  * @param {Object} config The config object
37044  */
37045
37046 Roo.bootstrap.RadioSet = function(config){
37047     
37048     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37049     
37050     this.radioes = [];
37051     
37052     Roo.bootstrap.RadioSet.register(this);
37053     
37054     this.addEvents({
37055         /**
37056         * @event check
37057         * Fires when the element is checked or unchecked.
37058         * @param {Roo.bootstrap.RadioSet} this This radio
37059         * @param {Roo.bootstrap.Radio} item The checked item
37060         */
37061        check : true,
37062        /**
37063         * @event click
37064         * Fires when the element is click.
37065         * @param {Roo.bootstrap.RadioSet} this This radio set
37066         * @param {Roo.bootstrap.Radio} item The checked item
37067         * @param {Roo.EventObject} e The event object
37068         */
37069        click : true
37070     });
37071     
37072 };
37073
37074 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37075
37076     radioes : false,
37077     
37078     inline : true,
37079     
37080     weight : '',
37081     
37082     indicatorpos : 'left',
37083     
37084     getAutoCreate : function()
37085     {
37086         var label = {
37087             tag : 'label',
37088             cls : 'roo-radio-set-label',
37089             cn : [
37090                 {
37091                     tag : 'span',
37092                     html : this.fieldLabel
37093                 }
37094             ]
37095         };
37096         if (Roo.bootstrap.version == 3) {
37097             
37098             
37099             if(this.indicatorpos == 'left'){
37100                 label.cn.unshift({
37101                     tag : 'i',
37102                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37103                     tooltip : 'This field is required'
37104                 });
37105             } else {
37106                 label.cn.push({
37107                     tag : 'i',
37108                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37109                     tooltip : 'This field is required'
37110                 });
37111             }
37112         }
37113         var items = {
37114             tag : 'div',
37115             cls : 'roo-radio-set-items'
37116         };
37117         
37118         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37119         
37120         if (align === 'left' && this.fieldLabel.length) {
37121             
37122             items = {
37123                 cls : "roo-radio-set-right", 
37124                 cn: [
37125                     items
37126                 ]
37127             };
37128             
37129             if(this.labelWidth > 12){
37130                 label.style = "width: " + this.labelWidth + 'px';
37131             }
37132             
37133             if(this.labelWidth < 13 && this.labelmd == 0){
37134                 this.labelmd = this.labelWidth;
37135             }
37136             
37137             if(this.labellg > 0){
37138                 label.cls += ' col-lg-' + this.labellg;
37139                 items.cls += ' col-lg-' + (12 - this.labellg);
37140             }
37141             
37142             if(this.labelmd > 0){
37143                 label.cls += ' col-md-' + this.labelmd;
37144                 items.cls += ' col-md-' + (12 - this.labelmd);
37145             }
37146             
37147             if(this.labelsm > 0){
37148                 label.cls += ' col-sm-' + this.labelsm;
37149                 items.cls += ' col-sm-' + (12 - this.labelsm);
37150             }
37151             
37152             if(this.labelxs > 0){
37153                 label.cls += ' col-xs-' + this.labelxs;
37154                 items.cls += ' col-xs-' + (12 - this.labelxs);
37155             }
37156         }
37157         
37158         var cfg = {
37159             tag : 'div',
37160             cls : 'roo-radio-set',
37161             cn : [
37162                 {
37163                     tag : 'input',
37164                     cls : 'roo-radio-set-input',
37165                     type : 'hidden',
37166                     name : this.name,
37167                     value : this.value ? this.value :  ''
37168                 },
37169                 label,
37170                 items
37171             ]
37172         };
37173         
37174         if(this.weight.length){
37175             cfg.cls += ' roo-radio-' + this.weight;
37176         }
37177         
37178         if(this.inline) {
37179             cfg.cls += ' roo-radio-set-inline';
37180         }
37181         
37182         var settings=this;
37183         ['xs','sm','md','lg'].map(function(size){
37184             if (settings[size]) {
37185                 cfg.cls += ' col-' + size + '-' + settings[size];
37186             }
37187         });
37188         
37189         return cfg;
37190         
37191     },
37192
37193     initEvents : function()
37194     {
37195         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37196         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37197         
37198         if(!this.fieldLabel.length){
37199             this.labelEl.hide();
37200         }
37201         
37202         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37203         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37204         
37205         this.indicator = this.indicatorEl();
37206         
37207         if(this.indicator){
37208             this.indicator.addClass('invisible');
37209         }
37210         
37211         this.originalValue = this.getValue();
37212         
37213     },
37214     
37215     inputEl: function ()
37216     {
37217         return this.el.select('.roo-radio-set-input', true).first();
37218     },
37219     
37220     getChildContainer : function()
37221     {
37222         return this.itemsEl;
37223     },
37224     
37225     register : function(item)
37226     {
37227         this.radioes.push(item);
37228         
37229     },
37230     
37231     validate : function()
37232     {   
37233         if(this.getVisibilityEl().hasClass('hidden')){
37234             return true;
37235         }
37236         
37237         var valid = false;
37238         
37239         Roo.each(this.radioes, function(i){
37240             if(!i.checked){
37241                 return;
37242             }
37243             
37244             valid = true;
37245             return false;
37246         });
37247         
37248         if(this.allowBlank) {
37249             return true;
37250         }
37251         
37252         if(this.disabled || valid){
37253             this.markValid();
37254             return true;
37255         }
37256         
37257         this.markInvalid();
37258         return false;
37259         
37260     },
37261     
37262     markValid : function()
37263     {
37264         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37265             this.indicatorEl().removeClass('visible');
37266             this.indicatorEl().addClass('invisible');
37267         }
37268         
37269         
37270         if (Roo.bootstrap.version == 3) {
37271             this.el.removeClass([this.invalidClass, this.validClass]);
37272             this.el.addClass(this.validClass);
37273         } else {
37274             this.el.removeClass(['is-invalid','is-valid']);
37275             this.el.addClass(['is-valid']);
37276         }
37277         this.fireEvent('valid', this);
37278     },
37279     
37280     markInvalid : function(msg)
37281     {
37282         if(this.allowBlank || this.disabled){
37283             return;
37284         }
37285         
37286         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37287             this.indicatorEl().removeClass('invisible');
37288             this.indicatorEl().addClass('visible');
37289         }
37290         if (Roo.bootstrap.version == 3) {
37291             this.el.removeClass([this.invalidClass, this.validClass]);
37292             this.el.addClass(this.invalidClass);
37293         } else {
37294             this.el.removeClass(['is-invalid','is-valid']);
37295             this.el.addClass(['is-invalid']);
37296         }
37297         
37298         this.fireEvent('invalid', this, msg);
37299         
37300     },
37301     
37302     setValue : function(v, suppressEvent)
37303     {   
37304         if(this.value === v){
37305             return;
37306         }
37307         
37308         this.value = v;
37309         
37310         if(this.rendered){
37311             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37312         }
37313         
37314         Roo.each(this.radioes, function(i){
37315             i.checked = false;
37316             i.el.removeClass('checked');
37317         });
37318         
37319         Roo.each(this.radioes, function(i){
37320             
37321             if(i.value === v || i.value.toString() === v.toString()){
37322                 i.checked = true;
37323                 i.el.addClass('checked');
37324                 
37325                 if(suppressEvent !== true){
37326                     this.fireEvent('check', this, i);
37327                 }
37328                 
37329                 return false;
37330             }
37331             
37332         }, this);
37333         
37334         this.validate();
37335     },
37336     
37337     clearInvalid : function(){
37338         
37339         if(!this.el || this.preventMark){
37340             return;
37341         }
37342         
37343         this.el.removeClass([this.invalidClass]);
37344         
37345         this.fireEvent('valid', this);
37346     }
37347     
37348 });
37349
37350 Roo.apply(Roo.bootstrap.RadioSet, {
37351     
37352     groups: {},
37353     
37354     register : function(set)
37355     {
37356         this.groups[set.name] = set;
37357     },
37358     
37359     get: function(name) 
37360     {
37361         if (typeof(this.groups[name]) == 'undefined') {
37362             return false;
37363         }
37364         
37365         return this.groups[name] ;
37366     }
37367     
37368 });
37369 /*
37370  * Based on:
37371  * Ext JS Library 1.1.1
37372  * Copyright(c) 2006-2007, Ext JS, LLC.
37373  *
37374  * Originally Released Under LGPL - original licence link has changed is not relivant.
37375  *
37376  * Fork - LGPL
37377  * <script type="text/javascript">
37378  */
37379
37380
37381 /**
37382  * @class Roo.bootstrap.SplitBar
37383  * @extends Roo.util.Observable
37384  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37385  * <br><br>
37386  * Usage:
37387  * <pre><code>
37388 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37389                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37390 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37391 split.minSize = 100;
37392 split.maxSize = 600;
37393 split.animate = true;
37394 split.on('moved', splitterMoved);
37395 </code></pre>
37396  * @constructor
37397  * Create a new SplitBar
37398  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37399  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37400  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37401  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37402                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37403                         position of the SplitBar).
37404  */
37405 Roo.bootstrap.SplitBar = function(cfg){
37406     
37407     /** @private */
37408     
37409     //{
37410     //  dragElement : elm
37411     //  resizingElement: el,
37412         // optional..
37413     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37414     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37415         // existingProxy ???
37416     //}
37417     
37418     this.el = Roo.get(cfg.dragElement, true);
37419     this.el.dom.unselectable = "on";
37420     /** @private */
37421     this.resizingEl = Roo.get(cfg.resizingElement, true);
37422
37423     /**
37424      * @private
37425      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37426      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37427      * @type Number
37428      */
37429     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37430     
37431     /**
37432      * The minimum size of the resizing element. (Defaults to 0)
37433      * @type Number
37434      */
37435     this.minSize = 0;
37436     
37437     /**
37438      * The maximum size of the resizing element. (Defaults to 2000)
37439      * @type Number
37440      */
37441     this.maxSize = 2000;
37442     
37443     /**
37444      * Whether to animate the transition to the new size
37445      * @type Boolean
37446      */
37447     this.animate = false;
37448     
37449     /**
37450      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37451      * @type Boolean
37452      */
37453     this.useShim = false;
37454     
37455     /** @private */
37456     this.shim = null;
37457     
37458     if(!cfg.existingProxy){
37459         /** @private */
37460         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37461     }else{
37462         this.proxy = Roo.get(cfg.existingProxy).dom;
37463     }
37464     /** @private */
37465     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37466     
37467     /** @private */
37468     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37469     
37470     /** @private */
37471     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37472     
37473     /** @private */
37474     this.dragSpecs = {};
37475     
37476     /**
37477      * @private The adapter to use to positon and resize elements
37478      */
37479     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37480     this.adapter.init(this);
37481     
37482     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37483         /** @private */
37484         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37485         this.el.addClass("roo-splitbar-h");
37486     }else{
37487         /** @private */
37488         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37489         this.el.addClass("roo-splitbar-v");
37490     }
37491     
37492     this.addEvents({
37493         /**
37494          * @event resize
37495          * Fires when the splitter is moved (alias for {@link #event-moved})
37496          * @param {Roo.bootstrap.SplitBar} this
37497          * @param {Number} newSize the new width or height
37498          */
37499         "resize" : true,
37500         /**
37501          * @event moved
37502          * Fires when the splitter is moved
37503          * @param {Roo.bootstrap.SplitBar} this
37504          * @param {Number} newSize the new width or height
37505          */
37506         "moved" : true,
37507         /**
37508          * @event beforeresize
37509          * Fires before the splitter is dragged
37510          * @param {Roo.bootstrap.SplitBar} this
37511          */
37512         "beforeresize" : true,
37513
37514         "beforeapply" : true
37515     });
37516
37517     Roo.util.Observable.call(this);
37518 };
37519
37520 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37521     onStartProxyDrag : function(x, y){
37522         this.fireEvent("beforeresize", this);
37523         if(!this.overlay){
37524             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37525             o.unselectable();
37526             o.enableDisplayMode("block");
37527             // all splitbars share the same overlay
37528             Roo.bootstrap.SplitBar.prototype.overlay = o;
37529         }
37530         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37531         this.overlay.show();
37532         Roo.get(this.proxy).setDisplayed("block");
37533         var size = this.adapter.getElementSize(this);
37534         this.activeMinSize = this.getMinimumSize();;
37535         this.activeMaxSize = this.getMaximumSize();;
37536         var c1 = size - this.activeMinSize;
37537         var c2 = Math.max(this.activeMaxSize - size, 0);
37538         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37539             this.dd.resetConstraints();
37540             this.dd.setXConstraint(
37541                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37542                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37543             );
37544             this.dd.setYConstraint(0, 0);
37545         }else{
37546             this.dd.resetConstraints();
37547             this.dd.setXConstraint(0, 0);
37548             this.dd.setYConstraint(
37549                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37550                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37551             );
37552          }
37553         this.dragSpecs.startSize = size;
37554         this.dragSpecs.startPoint = [x, y];
37555         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37556     },
37557     
37558     /** 
37559      * @private Called after the drag operation by the DDProxy
37560      */
37561     onEndProxyDrag : function(e){
37562         Roo.get(this.proxy).setDisplayed(false);
37563         var endPoint = Roo.lib.Event.getXY(e);
37564         if(this.overlay){
37565             this.overlay.hide();
37566         }
37567         var newSize;
37568         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37569             newSize = this.dragSpecs.startSize + 
37570                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37571                     endPoint[0] - this.dragSpecs.startPoint[0] :
37572                     this.dragSpecs.startPoint[0] - endPoint[0]
37573                 );
37574         }else{
37575             newSize = this.dragSpecs.startSize + 
37576                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37577                     endPoint[1] - this.dragSpecs.startPoint[1] :
37578                     this.dragSpecs.startPoint[1] - endPoint[1]
37579                 );
37580         }
37581         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37582         if(newSize != this.dragSpecs.startSize){
37583             if(this.fireEvent('beforeapply', this, newSize) !== false){
37584                 this.adapter.setElementSize(this, newSize);
37585                 this.fireEvent("moved", this, newSize);
37586                 this.fireEvent("resize", this, newSize);
37587             }
37588         }
37589     },
37590     
37591     /**
37592      * Get the adapter this SplitBar uses
37593      * @return The adapter object
37594      */
37595     getAdapter : function(){
37596         return this.adapter;
37597     },
37598     
37599     /**
37600      * Set the adapter this SplitBar uses
37601      * @param {Object} adapter A SplitBar adapter object
37602      */
37603     setAdapter : function(adapter){
37604         this.adapter = adapter;
37605         this.adapter.init(this);
37606     },
37607     
37608     /**
37609      * Gets the minimum size for the resizing element
37610      * @return {Number} The minimum size
37611      */
37612     getMinimumSize : function(){
37613         return this.minSize;
37614     },
37615     
37616     /**
37617      * Sets the minimum size for the resizing element
37618      * @param {Number} minSize The minimum size
37619      */
37620     setMinimumSize : function(minSize){
37621         this.minSize = minSize;
37622     },
37623     
37624     /**
37625      * Gets the maximum size for the resizing element
37626      * @return {Number} The maximum size
37627      */
37628     getMaximumSize : function(){
37629         return this.maxSize;
37630     },
37631     
37632     /**
37633      * Sets the maximum size for the resizing element
37634      * @param {Number} maxSize The maximum size
37635      */
37636     setMaximumSize : function(maxSize){
37637         this.maxSize = maxSize;
37638     },
37639     
37640     /**
37641      * Sets the initialize size for the resizing element
37642      * @param {Number} size The initial size
37643      */
37644     setCurrentSize : function(size){
37645         var oldAnimate = this.animate;
37646         this.animate = false;
37647         this.adapter.setElementSize(this, size);
37648         this.animate = oldAnimate;
37649     },
37650     
37651     /**
37652      * Destroy this splitbar. 
37653      * @param {Boolean} removeEl True to remove the element
37654      */
37655     destroy : function(removeEl){
37656         if(this.shim){
37657             this.shim.remove();
37658         }
37659         this.dd.unreg();
37660         this.proxy.parentNode.removeChild(this.proxy);
37661         if(removeEl){
37662             this.el.remove();
37663         }
37664     }
37665 });
37666
37667 /**
37668  * @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.
37669  */
37670 Roo.bootstrap.SplitBar.createProxy = function(dir){
37671     var proxy = new Roo.Element(document.createElement("div"));
37672     proxy.unselectable();
37673     var cls = 'roo-splitbar-proxy';
37674     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37675     document.body.appendChild(proxy.dom);
37676     return proxy.dom;
37677 };
37678
37679 /** 
37680  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37681  * Default Adapter. It assumes the splitter and resizing element are not positioned
37682  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37683  */
37684 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37685 };
37686
37687 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37688     // do nothing for now
37689     init : function(s){
37690     
37691     },
37692     /**
37693      * Called before drag operations to get the current size of the resizing element. 
37694      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37695      */
37696      getElementSize : function(s){
37697         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37698             return s.resizingEl.getWidth();
37699         }else{
37700             return s.resizingEl.getHeight();
37701         }
37702     },
37703     
37704     /**
37705      * Called after drag operations to set the size of the resizing element.
37706      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37707      * @param {Number} newSize The new size to set
37708      * @param {Function} onComplete A function to be invoked when resizing is complete
37709      */
37710     setElementSize : function(s, newSize, onComplete){
37711         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37712             if(!s.animate){
37713                 s.resizingEl.setWidth(newSize);
37714                 if(onComplete){
37715                     onComplete(s, newSize);
37716                 }
37717             }else{
37718                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37719             }
37720         }else{
37721             
37722             if(!s.animate){
37723                 s.resizingEl.setHeight(newSize);
37724                 if(onComplete){
37725                     onComplete(s, newSize);
37726                 }
37727             }else{
37728                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37729             }
37730         }
37731     }
37732 };
37733
37734 /** 
37735  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37736  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37737  * Adapter that  moves the splitter element to align with the resized sizing element. 
37738  * Used with an absolute positioned SplitBar.
37739  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37740  * document.body, make sure you assign an id to the body element.
37741  */
37742 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37743     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37744     this.container = Roo.get(container);
37745 };
37746
37747 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37748     init : function(s){
37749         this.basic.init(s);
37750     },
37751     
37752     getElementSize : function(s){
37753         return this.basic.getElementSize(s);
37754     },
37755     
37756     setElementSize : function(s, newSize, onComplete){
37757         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37758     },
37759     
37760     moveSplitter : function(s){
37761         var yes = Roo.bootstrap.SplitBar;
37762         switch(s.placement){
37763             case yes.LEFT:
37764                 s.el.setX(s.resizingEl.getRight());
37765                 break;
37766             case yes.RIGHT:
37767                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37768                 break;
37769             case yes.TOP:
37770                 s.el.setY(s.resizingEl.getBottom());
37771                 break;
37772             case yes.BOTTOM:
37773                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37774                 break;
37775         }
37776     }
37777 };
37778
37779 /**
37780  * Orientation constant - Create a vertical SplitBar
37781  * @static
37782  * @type Number
37783  */
37784 Roo.bootstrap.SplitBar.VERTICAL = 1;
37785
37786 /**
37787  * Orientation constant - Create a horizontal SplitBar
37788  * @static
37789  * @type Number
37790  */
37791 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37792
37793 /**
37794  * Placement constant - The resizing element is to the left of the splitter element
37795  * @static
37796  * @type Number
37797  */
37798 Roo.bootstrap.SplitBar.LEFT = 1;
37799
37800 /**
37801  * Placement constant - The resizing element is to the right of the splitter element
37802  * @static
37803  * @type Number
37804  */
37805 Roo.bootstrap.SplitBar.RIGHT = 2;
37806
37807 /**
37808  * Placement constant - The resizing element is positioned above the splitter element
37809  * @static
37810  * @type Number
37811  */
37812 Roo.bootstrap.SplitBar.TOP = 3;
37813
37814 /**
37815  * Placement constant - The resizing element is positioned under splitter element
37816  * @static
37817  * @type Number
37818  */
37819 Roo.bootstrap.SplitBar.BOTTOM = 4;
37820 Roo.namespace("Roo.bootstrap.layout");/*
37821  * Based on:
37822  * Ext JS Library 1.1.1
37823  * Copyright(c) 2006-2007, Ext JS, LLC.
37824  *
37825  * Originally Released Under LGPL - original licence link has changed is not relivant.
37826  *
37827  * Fork - LGPL
37828  * <script type="text/javascript">
37829  */
37830
37831 /**
37832  * @class Roo.bootstrap.layout.Manager
37833  * @extends Roo.bootstrap.Component
37834  * Base class for layout managers.
37835  */
37836 Roo.bootstrap.layout.Manager = function(config)
37837 {
37838     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37839
37840
37841
37842
37843
37844     /** false to disable window resize monitoring @type Boolean */
37845     this.monitorWindowResize = true;
37846     this.regions = {};
37847     this.addEvents({
37848         /**
37849          * @event layout
37850          * Fires when a layout is performed.
37851          * @param {Roo.LayoutManager} this
37852          */
37853         "layout" : true,
37854         /**
37855          * @event regionresized
37856          * Fires when the user resizes a region.
37857          * @param {Roo.LayoutRegion} region The resized region
37858          * @param {Number} newSize The new size (width for east/west, height for north/south)
37859          */
37860         "regionresized" : true,
37861         /**
37862          * @event regioncollapsed
37863          * Fires when a region is collapsed.
37864          * @param {Roo.LayoutRegion} region The collapsed region
37865          */
37866         "regioncollapsed" : true,
37867         /**
37868          * @event regionexpanded
37869          * Fires when a region is expanded.
37870          * @param {Roo.LayoutRegion} region The expanded region
37871          */
37872         "regionexpanded" : true
37873     });
37874     this.updating = false;
37875
37876     if (config.el) {
37877         this.el = Roo.get(config.el);
37878         this.initEvents();
37879     }
37880
37881 };
37882
37883 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37884
37885
37886     regions : null,
37887
37888     monitorWindowResize : true,
37889
37890
37891     updating : false,
37892
37893
37894     onRender : function(ct, position)
37895     {
37896         if(!this.el){
37897             this.el = Roo.get(ct);
37898             this.initEvents();
37899         }
37900         //this.fireEvent('render',this);
37901     },
37902
37903
37904     initEvents: function()
37905     {
37906
37907
37908         // ie scrollbar fix
37909         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37910             document.body.scroll = "no";
37911         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37912             this.el.position('relative');
37913         }
37914         this.id = this.el.id;
37915         this.el.addClass("roo-layout-container");
37916         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37917         if(this.el.dom != document.body ) {
37918             this.el.on('resize', this.layout,this);
37919             this.el.on('show', this.layout,this);
37920         }
37921
37922     },
37923
37924     /**
37925      * Returns true if this layout is currently being updated
37926      * @return {Boolean}
37927      */
37928     isUpdating : function(){
37929         return this.updating;
37930     },
37931
37932     /**
37933      * Suspend the LayoutManager from doing auto-layouts while
37934      * making multiple add or remove calls
37935      */
37936     beginUpdate : function(){
37937         this.updating = true;
37938     },
37939
37940     /**
37941      * Restore auto-layouts and optionally disable the manager from performing a layout
37942      * @param {Boolean} noLayout true to disable a layout update
37943      */
37944     endUpdate : function(noLayout){
37945         this.updating = false;
37946         if(!noLayout){
37947             this.layout();
37948         }
37949     },
37950
37951     layout: function(){
37952         // abstract...
37953     },
37954
37955     onRegionResized : function(region, newSize){
37956         this.fireEvent("regionresized", region, newSize);
37957         this.layout();
37958     },
37959
37960     onRegionCollapsed : function(region){
37961         this.fireEvent("regioncollapsed", region);
37962     },
37963
37964     onRegionExpanded : function(region){
37965         this.fireEvent("regionexpanded", region);
37966     },
37967
37968     /**
37969      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37970      * performs box-model adjustments.
37971      * @return {Object} The size as an object {width: (the width), height: (the height)}
37972      */
37973     getViewSize : function()
37974     {
37975         var size;
37976         if(this.el.dom != document.body){
37977             size = this.el.getSize();
37978         }else{
37979             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37980         }
37981         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37982         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37983         return size;
37984     },
37985
37986     /**
37987      * Returns the Element this layout is bound to.
37988      * @return {Roo.Element}
37989      */
37990     getEl : function(){
37991         return this.el;
37992     },
37993
37994     /**
37995      * Returns the specified region.
37996      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37997      * @return {Roo.LayoutRegion}
37998      */
37999     getRegion : function(target){
38000         return this.regions[target.toLowerCase()];
38001     },
38002
38003     onWindowResize : function(){
38004         if(this.monitorWindowResize){
38005             this.layout();
38006         }
38007     }
38008 });
38009 /*
38010  * Based on:
38011  * Ext JS Library 1.1.1
38012  * Copyright(c) 2006-2007, Ext JS, LLC.
38013  *
38014  * Originally Released Under LGPL - original licence link has changed is not relivant.
38015  *
38016  * Fork - LGPL
38017  * <script type="text/javascript">
38018  */
38019 /**
38020  * @class Roo.bootstrap.layout.Border
38021  * @extends Roo.bootstrap.layout.Manager
38022  * @builder-top
38023  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38024  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38025  * please see: examples/bootstrap/nested.html<br><br>
38026  
38027 <b>The container the layout is rendered into can be either the body element or any other element.
38028 If it is not the body element, the container needs to either be an absolute positioned element,
38029 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38030 the container size if it is not the body element.</b>
38031
38032 * @constructor
38033 * Create a new Border
38034 * @param {Object} config Configuration options
38035  */
38036 Roo.bootstrap.layout.Border = function(config){
38037     config = config || {};
38038     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38039     
38040     
38041     
38042     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38043         if(config[region]){
38044             config[region].region = region;
38045             this.addRegion(config[region]);
38046         }
38047     },this);
38048     
38049 };
38050
38051 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38052
38053 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38054     
38055         /**
38056          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38057          */
38058         /**
38059          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38060          */
38061         /**
38062          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38063          */
38064         /**
38065          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38066          */
38067         /**
38068          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38069          */
38070         
38071         
38072         
38073         
38074     parent : false, // this might point to a 'nest' or a ???
38075     
38076     /**
38077      * Creates and adds a new region if it doesn't already exist.
38078      * @param {String} target The target region key (north, south, east, west or center).
38079      * @param {Object} config The regions config object
38080      * @return {BorderLayoutRegion} The new region
38081      */
38082     addRegion : function(config)
38083     {
38084         if(!this.regions[config.region]){
38085             var r = this.factory(config);
38086             this.bindRegion(r);
38087         }
38088         return this.regions[config.region];
38089     },
38090
38091     // private (kinda)
38092     bindRegion : function(r){
38093         this.regions[r.config.region] = r;
38094         
38095         r.on("visibilitychange",    this.layout, this);
38096         r.on("paneladded",          this.layout, this);
38097         r.on("panelremoved",        this.layout, this);
38098         r.on("invalidated",         this.layout, this);
38099         r.on("resized",             this.onRegionResized, this);
38100         r.on("collapsed",           this.onRegionCollapsed, this);
38101         r.on("expanded",            this.onRegionExpanded, this);
38102     },
38103
38104     /**
38105      * Performs a layout update.
38106      */
38107     layout : function()
38108     {
38109         if(this.updating) {
38110             return;
38111         }
38112         
38113         // render all the rebions if they have not been done alreayd?
38114         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38115             if(this.regions[region] && !this.regions[region].bodyEl){
38116                 this.regions[region].onRender(this.el)
38117             }
38118         },this);
38119         
38120         var size = this.getViewSize();
38121         var w = size.width;
38122         var h = size.height;
38123         var centerW = w;
38124         var centerH = h;
38125         var centerY = 0;
38126         var centerX = 0;
38127         //var x = 0, y = 0;
38128
38129         var rs = this.regions;
38130         var north = rs["north"];
38131         var south = rs["south"]; 
38132         var west = rs["west"];
38133         var east = rs["east"];
38134         var center = rs["center"];
38135         //if(this.hideOnLayout){ // not supported anymore
38136             //c.el.setStyle("display", "none");
38137         //}
38138         if(north && north.isVisible()){
38139             var b = north.getBox();
38140             var m = north.getMargins();
38141             b.width = w - (m.left+m.right);
38142             b.x = m.left;
38143             b.y = m.top;
38144             centerY = b.height + b.y + m.bottom;
38145             centerH -= centerY;
38146             north.updateBox(this.safeBox(b));
38147         }
38148         if(south && south.isVisible()){
38149             var b = south.getBox();
38150             var m = south.getMargins();
38151             b.width = w - (m.left+m.right);
38152             b.x = m.left;
38153             var totalHeight = (b.height + m.top + m.bottom);
38154             b.y = h - totalHeight + m.top;
38155             centerH -= totalHeight;
38156             south.updateBox(this.safeBox(b));
38157         }
38158         if(west && west.isVisible()){
38159             var b = west.getBox();
38160             var m = west.getMargins();
38161             b.height = centerH - (m.top+m.bottom);
38162             b.x = m.left;
38163             b.y = centerY + m.top;
38164             var totalWidth = (b.width + m.left + m.right);
38165             centerX += totalWidth;
38166             centerW -= totalWidth;
38167             west.updateBox(this.safeBox(b));
38168         }
38169         if(east && east.isVisible()){
38170             var b = east.getBox();
38171             var m = east.getMargins();
38172             b.height = centerH - (m.top+m.bottom);
38173             var totalWidth = (b.width + m.left + m.right);
38174             b.x = w - totalWidth + m.left;
38175             b.y = centerY + m.top;
38176             centerW -= totalWidth;
38177             east.updateBox(this.safeBox(b));
38178         }
38179         if(center){
38180             var m = center.getMargins();
38181             var centerBox = {
38182                 x: centerX + m.left,
38183                 y: centerY + m.top,
38184                 width: centerW - (m.left+m.right),
38185                 height: centerH - (m.top+m.bottom)
38186             };
38187             //if(this.hideOnLayout){
38188                 //center.el.setStyle("display", "block");
38189             //}
38190             center.updateBox(this.safeBox(centerBox));
38191         }
38192         this.el.repaint();
38193         this.fireEvent("layout", this);
38194     },
38195
38196     // private
38197     safeBox : function(box){
38198         box.width = Math.max(0, box.width);
38199         box.height = Math.max(0, box.height);
38200         return box;
38201     },
38202
38203     /**
38204      * Adds a ContentPanel (or subclass) to this layout.
38205      * @param {String} target The target region key (north, south, east, west or center).
38206      * @param {Roo.ContentPanel} panel The panel to add
38207      * @return {Roo.ContentPanel} The added panel
38208      */
38209     add : function(target, panel){
38210          
38211         target = target.toLowerCase();
38212         return this.regions[target].add(panel);
38213     },
38214
38215     /**
38216      * Remove a ContentPanel (or subclass) to this layout.
38217      * @param {String} target The target region key (north, south, east, west or center).
38218      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38219      * @return {Roo.ContentPanel} The removed panel
38220      */
38221     remove : function(target, panel){
38222         target = target.toLowerCase();
38223         return this.regions[target].remove(panel);
38224     },
38225
38226     /**
38227      * Searches all regions for a panel with the specified id
38228      * @param {String} panelId
38229      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38230      */
38231     findPanel : function(panelId){
38232         var rs = this.regions;
38233         for(var target in rs){
38234             if(typeof rs[target] != "function"){
38235                 var p = rs[target].getPanel(panelId);
38236                 if(p){
38237                     return p;
38238                 }
38239             }
38240         }
38241         return null;
38242     },
38243
38244     /**
38245      * Searches all regions for a panel with the specified id and activates (shows) it.
38246      * @param {String/ContentPanel} panelId The panels id or the panel itself
38247      * @return {Roo.ContentPanel} The shown panel or null
38248      */
38249     showPanel : function(panelId) {
38250       var rs = this.regions;
38251       for(var target in rs){
38252          var r = rs[target];
38253          if(typeof r != "function"){
38254             if(r.hasPanel(panelId)){
38255                return r.showPanel(panelId);
38256             }
38257          }
38258       }
38259       return null;
38260    },
38261
38262    /**
38263      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38264      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38265      */
38266    /*
38267     restoreState : function(provider){
38268         if(!provider){
38269             provider = Roo.state.Manager;
38270         }
38271         var sm = new Roo.LayoutStateManager();
38272         sm.init(this, provider);
38273     },
38274 */
38275  
38276  
38277     /**
38278      * Adds a xtype elements to the layout.
38279      * <pre><code>
38280
38281 layout.addxtype({
38282        xtype : 'ContentPanel',
38283        region: 'west',
38284        items: [ .... ]
38285    }
38286 );
38287
38288 layout.addxtype({
38289         xtype : 'NestedLayoutPanel',
38290         region: 'west',
38291         layout: {
38292            center: { },
38293            west: { }   
38294         },
38295         items : [ ... list of content panels or nested layout panels.. ]
38296    }
38297 );
38298 </code></pre>
38299      * @param {Object} cfg Xtype definition of item to add.
38300      */
38301     addxtype : function(cfg)
38302     {
38303         // basically accepts a pannel...
38304         // can accept a layout region..!?!?
38305         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38306         
38307         
38308         // theory?  children can only be panels??
38309         
38310         //if (!cfg.xtype.match(/Panel$/)) {
38311         //    return false;
38312         //}
38313         var ret = false;
38314         
38315         if (typeof(cfg.region) == 'undefined') {
38316             Roo.log("Failed to add Panel, region was not set");
38317             Roo.log(cfg);
38318             return false;
38319         }
38320         var region = cfg.region;
38321         delete cfg.region;
38322         
38323           
38324         var xitems = [];
38325         if (cfg.items) {
38326             xitems = cfg.items;
38327             delete cfg.items;
38328         }
38329         var nb = false;
38330         
38331         if ( region == 'center') {
38332             Roo.log("Center: " + cfg.title);
38333         }
38334         
38335         
38336         switch(cfg.xtype) 
38337         {
38338             case 'Content':  // ContentPanel (el, cfg)
38339             case 'Scroll':  // ContentPanel (el, cfg)
38340             case 'View': 
38341                 cfg.autoCreate = cfg.autoCreate || true;
38342                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38343                 //} else {
38344                 //    var el = this.el.createChild();
38345                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38346                 //}
38347                 
38348                 this.add(region, ret);
38349                 break;
38350             
38351             /*
38352             case 'TreePanel': // our new panel!
38353                 cfg.el = this.el.createChild();
38354                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38355                 this.add(region, ret);
38356                 break;
38357             */
38358             
38359             case 'Nest': 
38360                 // create a new Layout (which is  a Border Layout...
38361                 
38362                 var clayout = cfg.layout;
38363                 clayout.el  = this.el.createChild();
38364                 clayout.items   = clayout.items  || [];
38365                 
38366                 delete cfg.layout;
38367                 
38368                 // replace this exitems with the clayout ones..
38369                 xitems = clayout.items;
38370                  
38371                 // force background off if it's in center...
38372                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38373                     cfg.background = false;
38374                 }
38375                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38376                 
38377                 
38378                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38379                 //console.log('adding nested layout panel '  + cfg.toSource());
38380                 this.add(region, ret);
38381                 nb = {}; /// find first...
38382                 break;
38383             
38384             case 'Grid':
38385                 
38386                 // needs grid and region
38387                 
38388                 //var el = this.getRegion(region).el.createChild();
38389                 /*
38390                  *var el = this.el.createChild();
38391                 // create the grid first...
38392                 cfg.grid.container = el;
38393                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38394                 */
38395                 
38396                 if (region == 'center' && this.active ) {
38397                     cfg.background = false;
38398                 }
38399                 
38400                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38401                 
38402                 this.add(region, ret);
38403                 /*
38404                 if (cfg.background) {
38405                     // render grid on panel activation (if panel background)
38406                     ret.on('activate', function(gp) {
38407                         if (!gp.grid.rendered) {
38408                     //        gp.grid.render(el);
38409                         }
38410                     });
38411                 } else {
38412                   //  cfg.grid.render(el);
38413                 }
38414                 */
38415                 break;
38416            
38417            
38418             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38419                 // it was the old xcomponent building that caused this before.
38420                 // espeically if border is the top element in the tree.
38421                 ret = this;
38422                 break; 
38423                 
38424                     
38425                 
38426                 
38427                 
38428             default:
38429                 /*
38430                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38431                     
38432                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38433                     this.add(region, ret);
38434                 } else {
38435                 */
38436                     Roo.log(cfg);
38437                     throw "Can not add '" + cfg.xtype + "' to Border";
38438                     return null;
38439              
38440                                 
38441              
38442         }
38443         this.beginUpdate();
38444         // add children..
38445         var region = '';
38446         var abn = {};
38447         Roo.each(xitems, function(i)  {
38448             region = nb && i.region ? i.region : false;
38449             
38450             var add = ret.addxtype(i);
38451            
38452             if (region) {
38453                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38454                 if (!i.background) {
38455                     abn[region] = nb[region] ;
38456                 }
38457             }
38458             
38459         });
38460         this.endUpdate();
38461
38462         // make the last non-background panel active..
38463         //if (nb) { Roo.log(abn); }
38464         if (nb) {
38465             
38466             for(var r in abn) {
38467                 region = this.getRegion(r);
38468                 if (region) {
38469                     // tried using nb[r], but it does not work..
38470                      
38471                     region.showPanel(abn[r]);
38472                    
38473                 }
38474             }
38475         }
38476         return ret;
38477         
38478     },
38479     
38480     
38481 // private
38482     factory : function(cfg)
38483     {
38484         
38485         var validRegions = Roo.bootstrap.layout.Border.regions;
38486
38487         var target = cfg.region;
38488         cfg.mgr = this;
38489         
38490         var r = Roo.bootstrap.layout;
38491         Roo.log(target);
38492         switch(target){
38493             case "north":
38494                 return new r.North(cfg);
38495             case "south":
38496                 return new r.South(cfg);
38497             case "east":
38498                 return new r.East(cfg);
38499             case "west":
38500                 return new r.West(cfg);
38501             case "center":
38502                 return new r.Center(cfg);
38503         }
38504         throw 'Layout region "'+target+'" not supported.';
38505     }
38506     
38507     
38508 });
38509  /*
38510  * Based on:
38511  * Ext JS Library 1.1.1
38512  * Copyright(c) 2006-2007, Ext JS, LLC.
38513  *
38514  * Originally Released Under LGPL - original licence link has changed is not relivant.
38515  *
38516  * Fork - LGPL
38517  * <script type="text/javascript">
38518  */
38519  
38520 /**
38521  * @class Roo.bootstrap.layout.Basic
38522  * @extends Roo.util.Observable
38523  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38524  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38525  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38526  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38527  * @cfg {string}   region  the region that it inhabits..
38528  * @cfg {bool}   skipConfig skip config?
38529  * 
38530
38531  */
38532 Roo.bootstrap.layout.Basic = function(config){
38533     
38534     this.mgr = config.mgr;
38535     
38536     this.position = config.region;
38537     
38538     var skipConfig = config.skipConfig;
38539     
38540     this.events = {
38541         /**
38542          * @scope Roo.BasicLayoutRegion
38543          */
38544         
38545         /**
38546          * @event beforeremove
38547          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38548          * @param {Roo.LayoutRegion} this
38549          * @param {Roo.ContentPanel} panel The panel
38550          * @param {Object} e The cancel event object
38551          */
38552         "beforeremove" : true,
38553         /**
38554          * @event invalidated
38555          * Fires when the layout for this region is changed.
38556          * @param {Roo.LayoutRegion} this
38557          */
38558         "invalidated" : true,
38559         /**
38560          * @event visibilitychange
38561          * Fires when this region is shown or hidden 
38562          * @param {Roo.LayoutRegion} this
38563          * @param {Boolean} visibility true or false
38564          */
38565         "visibilitychange" : true,
38566         /**
38567          * @event paneladded
38568          * Fires when a panel is added. 
38569          * @param {Roo.LayoutRegion} this
38570          * @param {Roo.ContentPanel} panel The panel
38571          */
38572         "paneladded" : true,
38573         /**
38574          * @event panelremoved
38575          * Fires when a panel is removed. 
38576          * @param {Roo.LayoutRegion} this
38577          * @param {Roo.ContentPanel} panel The panel
38578          */
38579         "panelremoved" : true,
38580         /**
38581          * @event beforecollapse
38582          * Fires when this region before collapse.
38583          * @param {Roo.LayoutRegion} this
38584          */
38585         "beforecollapse" : true,
38586         /**
38587          * @event collapsed
38588          * Fires when this region is collapsed.
38589          * @param {Roo.LayoutRegion} this
38590          */
38591         "collapsed" : true,
38592         /**
38593          * @event expanded
38594          * Fires when this region is expanded.
38595          * @param {Roo.LayoutRegion} this
38596          */
38597         "expanded" : true,
38598         /**
38599          * @event slideshow
38600          * Fires when this region is slid into view.
38601          * @param {Roo.LayoutRegion} this
38602          */
38603         "slideshow" : true,
38604         /**
38605          * @event slidehide
38606          * Fires when this region slides out of view. 
38607          * @param {Roo.LayoutRegion} this
38608          */
38609         "slidehide" : true,
38610         /**
38611          * @event panelactivated
38612          * Fires when a panel is activated. 
38613          * @param {Roo.LayoutRegion} this
38614          * @param {Roo.ContentPanel} panel The activated panel
38615          */
38616         "panelactivated" : true,
38617         /**
38618          * @event resized
38619          * Fires when the user resizes this region. 
38620          * @param {Roo.LayoutRegion} this
38621          * @param {Number} newSize The new size (width for east/west, height for north/south)
38622          */
38623         "resized" : true
38624     };
38625     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38626     this.panels = new Roo.util.MixedCollection();
38627     this.panels.getKey = this.getPanelId.createDelegate(this);
38628     this.box = null;
38629     this.activePanel = null;
38630     // ensure listeners are added...
38631     
38632     if (config.listeners || config.events) {
38633         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38634             listeners : config.listeners || {},
38635             events : config.events || {}
38636         });
38637     }
38638     
38639     if(skipConfig !== true){
38640         this.applyConfig(config);
38641     }
38642 };
38643
38644 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38645 {
38646     getPanelId : function(p){
38647         return p.getId();
38648     },
38649     
38650     applyConfig : function(config){
38651         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38652         this.config = config;
38653         
38654     },
38655     
38656     /**
38657      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38658      * the width, for horizontal (north, south) the height.
38659      * @param {Number} newSize The new width or height
38660      */
38661     resizeTo : function(newSize){
38662         var el = this.el ? this.el :
38663                  (this.activePanel ? this.activePanel.getEl() : null);
38664         if(el){
38665             switch(this.position){
38666                 case "east":
38667                 case "west":
38668                     el.setWidth(newSize);
38669                     this.fireEvent("resized", this, newSize);
38670                 break;
38671                 case "north":
38672                 case "south":
38673                     el.setHeight(newSize);
38674                     this.fireEvent("resized", this, newSize);
38675                 break;                
38676             }
38677         }
38678     },
38679     
38680     getBox : function(){
38681         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38682     },
38683     
38684     getMargins : function(){
38685         return this.margins;
38686     },
38687     
38688     updateBox : function(box){
38689         this.box = box;
38690         var el = this.activePanel.getEl();
38691         el.dom.style.left = box.x + "px";
38692         el.dom.style.top = box.y + "px";
38693         this.activePanel.setSize(box.width, box.height);
38694     },
38695     
38696     /**
38697      * Returns the container element for this region.
38698      * @return {Roo.Element}
38699      */
38700     getEl : function(){
38701         return this.activePanel;
38702     },
38703     
38704     /**
38705      * Returns true if this region is currently visible.
38706      * @return {Boolean}
38707      */
38708     isVisible : function(){
38709         return this.activePanel ? true : false;
38710     },
38711     
38712     setActivePanel : function(panel){
38713         panel = this.getPanel(panel);
38714         if(this.activePanel && this.activePanel != panel){
38715             this.activePanel.setActiveState(false);
38716             this.activePanel.getEl().setLeftTop(-10000,-10000);
38717         }
38718         this.activePanel = panel;
38719         panel.setActiveState(true);
38720         if(this.box){
38721             panel.setSize(this.box.width, this.box.height);
38722         }
38723         this.fireEvent("panelactivated", this, panel);
38724         this.fireEvent("invalidated");
38725     },
38726     
38727     /**
38728      * Show the specified panel.
38729      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38730      * @return {Roo.ContentPanel} The shown panel or null
38731      */
38732     showPanel : function(panel){
38733         panel = this.getPanel(panel);
38734         if(panel){
38735             this.setActivePanel(panel);
38736         }
38737         return panel;
38738     },
38739     
38740     /**
38741      * Get the active panel for this region.
38742      * @return {Roo.ContentPanel} The active panel or null
38743      */
38744     getActivePanel : function(){
38745         return this.activePanel;
38746     },
38747     
38748     /**
38749      * Add the passed ContentPanel(s)
38750      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38751      * @return {Roo.ContentPanel} The panel added (if only one was added)
38752      */
38753     add : function(panel){
38754         if(arguments.length > 1){
38755             for(var i = 0, len = arguments.length; i < len; i++) {
38756                 this.add(arguments[i]);
38757             }
38758             return null;
38759         }
38760         if(this.hasPanel(panel)){
38761             this.showPanel(panel);
38762             return panel;
38763         }
38764         var el = panel.getEl();
38765         if(el.dom.parentNode != this.mgr.el.dom){
38766             this.mgr.el.dom.appendChild(el.dom);
38767         }
38768         if(panel.setRegion){
38769             panel.setRegion(this);
38770         }
38771         this.panels.add(panel);
38772         el.setStyle("position", "absolute");
38773         if(!panel.background){
38774             this.setActivePanel(panel);
38775             if(this.config.initialSize && this.panels.getCount()==1){
38776                 this.resizeTo(this.config.initialSize);
38777             }
38778         }
38779         this.fireEvent("paneladded", this, panel);
38780         return panel;
38781     },
38782     
38783     /**
38784      * Returns true if the panel is in this region.
38785      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38786      * @return {Boolean}
38787      */
38788     hasPanel : function(panel){
38789         if(typeof panel == "object"){ // must be panel obj
38790             panel = panel.getId();
38791         }
38792         return this.getPanel(panel) ? true : false;
38793     },
38794     
38795     /**
38796      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38797      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38798      * @param {Boolean} preservePanel Overrides the config preservePanel option
38799      * @return {Roo.ContentPanel} The panel that was removed
38800      */
38801     remove : function(panel, preservePanel){
38802         panel = this.getPanel(panel);
38803         if(!panel){
38804             return null;
38805         }
38806         var e = {};
38807         this.fireEvent("beforeremove", this, panel, e);
38808         if(e.cancel === true){
38809             return null;
38810         }
38811         var panelId = panel.getId();
38812         this.panels.removeKey(panelId);
38813         return panel;
38814     },
38815     
38816     /**
38817      * Returns the panel specified or null if it's not in this region.
38818      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38819      * @return {Roo.ContentPanel}
38820      */
38821     getPanel : function(id){
38822         if(typeof id == "object"){ // must be panel obj
38823             return id;
38824         }
38825         return this.panels.get(id);
38826     },
38827     
38828     /**
38829      * Returns this regions position (north/south/east/west/center).
38830      * @return {String} 
38831      */
38832     getPosition: function(){
38833         return this.position;    
38834     }
38835 });/*
38836  * Based on:
38837  * Ext JS Library 1.1.1
38838  * Copyright(c) 2006-2007, Ext JS, LLC.
38839  *
38840  * Originally Released Under LGPL - original licence link has changed is not relivant.
38841  *
38842  * Fork - LGPL
38843  * <script type="text/javascript">
38844  */
38845  
38846 /**
38847  * @class Roo.bootstrap.layout.Region
38848  * @extends Roo.bootstrap.layout.Basic
38849  * This class represents a region in a layout manager.
38850  
38851  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38852  * @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})
38853  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38854  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38855  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38856  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38857  * @cfg {String}    title           The title for the region (overrides panel titles)
38858  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38859  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38860  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38861  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38862  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38863  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38864  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38865  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38866  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38867  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38868
38869  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38870  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38871  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38872  * @cfg {Number}    width           For East/West panels
38873  * @cfg {Number}    height          For North/South panels
38874  * @cfg {Boolean}   split           To show the splitter
38875  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38876  * 
38877  * @cfg {string}   cls             Extra CSS classes to add to region
38878  * 
38879  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38880  * @cfg {string}   region  the region that it inhabits..
38881  *
38882
38883  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38884  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38885
38886  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38887  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38888  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38889  */
38890 Roo.bootstrap.layout.Region = function(config)
38891 {
38892     this.applyConfig(config);
38893
38894     var mgr = config.mgr;
38895     var pos = config.region;
38896     config.skipConfig = true;
38897     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38898     
38899     if (mgr.el) {
38900         this.onRender(mgr.el);   
38901     }
38902      
38903     this.visible = true;
38904     this.collapsed = false;
38905     this.unrendered_panels = [];
38906 };
38907
38908 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38909
38910     position: '', // set by wrapper (eg. north/south etc..)
38911     unrendered_panels : null,  // unrendered panels.
38912     
38913     tabPosition : false,
38914     
38915     mgr: false, // points to 'Border'
38916     
38917     
38918     createBody : function(){
38919         /** This region's body element 
38920         * @type Roo.Element */
38921         this.bodyEl = this.el.createChild({
38922                 tag: "div",
38923                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38924         });
38925     },
38926
38927     onRender: function(ctr, pos)
38928     {
38929         var dh = Roo.DomHelper;
38930         /** This region's container element 
38931         * @type Roo.Element */
38932         this.el = dh.append(ctr.dom, {
38933                 tag: "div",
38934                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38935             }, true);
38936         /** This region's title element 
38937         * @type Roo.Element */
38938     
38939         this.titleEl = dh.append(this.el.dom,  {
38940                 tag: "div",
38941                 unselectable: "on",
38942                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38943                 children:[
38944                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38945                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38946                 ]
38947             }, true);
38948         
38949         this.titleEl.enableDisplayMode();
38950         /** This region's title text element 
38951         * @type HTMLElement */
38952         this.titleTextEl = this.titleEl.dom.firstChild;
38953         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38954         /*
38955         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38956         this.closeBtn.enableDisplayMode();
38957         this.closeBtn.on("click", this.closeClicked, this);
38958         this.closeBtn.hide();
38959     */
38960         this.createBody(this.config);
38961         if(this.config.hideWhenEmpty){
38962             this.hide();
38963             this.on("paneladded", this.validateVisibility, this);
38964             this.on("panelremoved", this.validateVisibility, this);
38965         }
38966         if(this.autoScroll){
38967             this.bodyEl.setStyle("overflow", "auto");
38968         }else{
38969             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38970         }
38971         //if(c.titlebar !== false){
38972             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38973                 this.titleEl.hide();
38974             }else{
38975                 this.titleEl.show();
38976                 if(this.config.title){
38977                     this.titleTextEl.innerHTML = this.config.title;
38978                 }
38979             }
38980         //}
38981         if(this.config.collapsed){
38982             this.collapse(true);
38983         }
38984         if(this.config.hidden){
38985             this.hide();
38986         }
38987         
38988         if (this.unrendered_panels && this.unrendered_panels.length) {
38989             for (var i =0;i< this.unrendered_panels.length; i++) {
38990                 this.add(this.unrendered_panels[i]);
38991             }
38992             this.unrendered_panels = null;
38993             
38994         }
38995         
38996     },
38997     
38998     applyConfig : function(c)
38999     {
39000         /*
39001          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39002             var dh = Roo.DomHelper;
39003             if(c.titlebar !== false){
39004                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39005                 this.collapseBtn.on("click", this.collapse, this);
39006                 this.collapseBtn.enableDisplayMode();
39007                 /*
39008                 if(c.showPin === true || this.showPin){
39009                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39010                     this.stickBtn.enableDisplayMode();
39011                     this.stickBtn.on("click", this.expand, this);
39012                     this.stickBtn.hide();
39013                 }
39014                 
39015             }
39016             */
39017             /** This region's collapsed element
39018             * @type Roo.Element */
39019             /*
39020              *
39021             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39022                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39023             ]}, true);
39024             
39025             if(c.floatable !== false){
39026                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39027                this.collapsedEl.on("click", this.collapseClick, this);
39028             }
39029
39030             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39031                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39032                    id: "message", unselectable: "on", style:{"float":"left"}});
39033                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39034              }
39035             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39036             this.expandBtn.on("click", this.expand, this);
39037             
39038         }
39039         
39040         if(this.collapseBtn){
39041             this.collapseBtn.setVisible(c.collapsible == true);
39042         }
39043         
39044         this.cmargins = c.cmargins || this.cmargins ||
39045                          (this.position == "west" || this.position == "east" ?
39046                              {top: 0, left: 2, right:2, bottom: 0} :
39047                              {top: 2, left: 0, right:0, bottom: 2});
39048         */
39049         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39050         
39051         
39052         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39053         
39054         this.autoScroll = c.autoScroll || false;
39055         
39056         
39057        
39058         
39059         this.duration = c.duration || .30;
39060         this.slideDuration = c.slideDuration || .45;
39061         this.config = c;
39062        
39063     },
39064     /**
39065      * Returns true if this region is currently visible.
39066      * @return {Boolean}
39067      */
39068     isVisible : function(){
39069         return this.visible;
39070     },
39071
39072     /**
39073      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39074      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39075      */
39076     //setCollapsedTitle : function(title){
39077     //    title = title || "&#160;";
39078      //   if(this.collapsedTitleTextEl){
39079       //      this.collapsedTitleTextEl.innerHTML = title;
39080        // }
39081     //},
39082
39083     getBox : function(){
39084         var b;
39085       //  if(!this.collapsed){
39086             b = this.el.getBox(false, true);
39087        // }else{
39088           //  b = this.collapsedEl.getBox(false, true);
39089         //}
39090         return b;
39091     },
39092
39093     getMargins : function(){
39094         return this.margins;
39095         //return this.collapsed ? this.cmargins : this.margins;
39096     },
39097 /*
39098     highlight : function(){
39099         this.el.addClass("x-layout-panel-dragover");
39100     },
39101
39102     unhighlight : function(){
39103         this.el.removeClass("x-layout-panel-dragover");
39104     },
39105 */
39106     updateBox : function(box)
39107     {
39108         if (!this.bodyEl) {
39109             return; // not rendered yet..
39110         }
39111         
39112         this.box = box;
39113         if(!this.collapsed){
39114             this.el.dom.style.left = box.x + "px";
39115             this.el.dom.style.top = box.y + "px";
39116             this.updateBody(box.width, box.height);
39117         }else{
39118             this.collapsedEl.dom.style.left = box.x + "px";
39119             this.collapsedEl.dom.style.top = box.y + "px";
39120             this.collapsedEl.setSize(box.width, box.height);
39121         }
39122         if(this.tabs){
39123             this.tabs.autoSizeTabs();
39124         }
39125     },
39126
39127     updateBody : function(w, h)
39128     {
39129         if(w !== null){
39130             this.el.setWidth(w);
39131             w -= this.el.getBorderWidth("rl");
39132             if(this.config.adjustments){
39133                 w += this.config.adjustments[0];
39134             }
39135         }
39136         if(h !== null && h > 0){
39137             this.el.setHeight(h);
39138             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39139             h -= this.el.getBorderWidth("tb");
39140             if(this.config.adjustments){
39141                 h += this.config.adjustments[1];
39142             }
39143             this.bodyEl.setHeight(h);
39144             if(this.tabs){
39145                 h = this.tabs.syncHeight(h);
39146             }
39147         }
39148         if(this.panelSize){
39149             w = w !== null ? w : this.panelSize.width;
39150             h = h !== null ? h : this.panelSize.height;
39151         }
39152         if(this.activePanel){
39153             var el = this.activePanel.getEl();
39154             w = w !== null ? w : el.getWidth();
39155             h = h !== null ? h : el.getHeight();
39156             this.panelSize = {width: w, height: h};
39157             this.activePanel.setSize(w, h);
39158         }
39159         if(Roo.isIE && this.tabs){
39160             this.tabs.el.repaint();
39161         }
39162     },
39163
39164     /**
39165      * Returns the container element for this region.
39166      * @return {Roo.Element}
39167      */
39168     getEl : function(){
39169         return this.el;
39170     },
39171
39172     /**
39173      * Hides this region.
39174      */
39175     hide : function(){
39176         //if(!this.collapsed){
39177             this.el.dom.style.left = "-2000px";
39178             this.el.hide();
39179         //}else{
39180          //   this.collapsedEl.dom.style.left = "-2000px";
39181          //   this.collapsedEl.hide();
39182        // }
39183         this.visible = false;
39184         this.fireEvent("visibilitychange", this, false);
39185     },
39186
39187     /**
39188      * Shows this region if it was previously hidden.
39189      */
39190     show : function(){
39191         //if(!this.collapsed){
39192             this.el.show();
39193         //}else{
39194         //    this.collapsedEl.show();
39195        // }
39196         this.visible = true;
39197         this.fireEvent("visibilitychange", this, true);
39198     },
39199 /*
39200     closeClicked : function(){
39201         if(this.activePanel){
39202             this.remove(this.activePanel);
39203         }
39204     },
39205
39206     collapseClick : function(e){
39207         if(this.isSlid){
39208            e.stopPropagation();
39209            this.slideIn();
39210         }else{
39211            e.stopPropagation();
39212            this.slideOut();
39213         }
39214     },
39215 */
39216     /**
39217      * Collapses this region.
39218      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39219      */
39220     /*
39221     collapse : function(skipAnim, skipCheck = false){
39222         if(this.collapsed) {
39223             return;
39224         }
39225         
39226         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39227             
39228             this.collapsed = true;
39229             if(this.split){
39230                 this.split.el.hide();
39231             }
39232             if(this.config.animate && skipAnim !== true){
39233                 this.fireEvent("invalidated", this);
39234                 this.animateCollapse();
39235             }else{
39236                 this.el.setLocation(-20000,-20000);
39237                 this.el.hide();
39238                 this.collapsedEl.show();
39239                 this.fireEvent("collapsed", this);
39240                 this.fireEvent("invalidated", this);
39241             }
39242         }
39243         
39244     },
39245 */
39246     animateCollapse : function(){
39247         // overridden
39248     },
39249
39250     /**
39251      * Expands this region if it was previously collapsed.
39252      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39253      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39254      */
39255     /*
39256     expand : function(e, skipAnim){
39257         if(e) {
39258             e.stopPropagation();
39259         }
39260         if(!this.collapsed || this.el.hasActiveFx()) {
39261             return;
39262         }
39263         if(this.isSlid){
39264             this.afterSlideIn();
39265             skipAnim = true;
39266         }
39267         this.collapsed = false;
39268         if(this.config.animate && skipAnim !== true){
39269             this.animateExpand();
39270         }else{
39271             this.el.show();
39272             if(this.split){
39273                 this.split.el.show();
39274             }
39275             this.collapsedEl.setLocation(-2000,-2000);
39276             this.collapsedEl.hide();
39277             this.fireEvent("invalidated", this);
39278             this.fireEvent("expanded", this);
39279         }
39280     },
39281 */
39282     animateExpand : function(){
39283         // overridden
39284     },
39285
39286     initTabs : function()
39287     {
39288         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39289         
39290         var ts = new Roo.bootstrap.panel.Tabs({
39291             el: this.bodyEl.dom,
39292             region : this,
39293             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39294             disableTooltips: this.config.disableTabTips,
39295             toolbar : this.config.toolbar
39296         });
39297         
39298         if(this.config.hideTabs){
39299             ts.stripWrap.setDisplayed(false);
39300         }
39301         this.tabs = ts;
39302         ts.resizeTabs = this.config.resizeTabs === true;
39303         ts.minTabWidth = this.config.minTabWidth || 40;
39304         ts.maxTabWidth = this.config.maxTabWidth || 250;
39305         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39306         ts.monitorResize = false;
39307         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39308         ts.bodyEl.addClass('roo-layout-tabs-body');
39309         this.panels.each(this.initPanelAsTab, this);
39310     },
39311
39312     initPanelAsTab : function(panel){
39313         var ti = this.tabs.addTab(
39314             panel.getEl().id,
39315             panel.getTitle(),
39316             null,
39317             this.config.closeOnTab && panel.isClosable(),
39318             panel.tpl
39319         );
39320         if(panel.tabTip !== undefined){
39321             ti.setTooltip(panel.tabTip);
39322         }
39323         ti.on("activate", function(){
39324               this.setActivePanel(panel);
39325         }, this);
39326         
39327         if(this.config.closeOnTab){
39328             ti.on("beforeclose", function(t, e){
39329                 e.cancel = true;
39330                 this.remove(panel);
39331             }, this);
39332         }
39333         
39334         panel.tabItem = ti;
39335         
39336         return ti;
39337     },
39338
39339     updatePanelTitle : function(panel, title)
39340     {
39341         if(this.activePanel == panel){
39342             this.updateTitle(title);
39343         }
39344         if(this.tabs){
39345             var ti = this.tabs.getTab(panel.getEl().id);
39346             ti.setText(title);
39347             if(panel.tabTip !== undefined){
39348                 ti.setTooltip(panel.tabTip);
39349             }
39350         }
39351     },
39352
39353     updateTitle : function(title){
39354         if(this.titleTextEl && !this.config.title){
39355             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39356         }
39357     },
39358
39359     setActivePanel : function(panel)
39360     {
39361         panel = this.getPanel(panel);
39362         if(this.activePanel && this.activePanel != panel){
39363             if(this.activePanel.setActiveState(false) === false){
39364                 return;
39365             }
39366         }
39367         this.activePanel = panel;
39368         panel.setActiveState(true);
39369         if(this.panelSize){
39370             panel.setSize(this.panelSize.width, this.panelSize.height);
39371         }
39372         if(this.closeBtn){
39373             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39374         }
39375         this.updateTitle(panel.getTitle());
39376         if(this.tabs){
39377             this.fireEvent("invalidated", this);
39378         }
39379         this.fireEvent("panelactivated", this, panel);
39380     },
39381
39382     /**
39383      * Shows the specified panel.
39384      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39385      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39386      */
39387     showPanel : function(panel)
39388     {
39389         panel = this.getPanel(panel);
39390         if(panel){
39391             if(this.tabs){
39392                 var tab = this.tabs.getTab(panel.getEl().id);
39393                 if(tab.isHidden()){
39394                     this.tabs.unhideTab(tab.id);
39395                 }
39396                 tab.activate();
39397             }else{
39398                 this.setActivePanel(panel);
39399             }
39400         }
39401         return panel;
39402     },
39403
39404     /**
39405      * Get the active panel for this region.
39406      * @return {Roo.ContentPanel} The active panel or null
39407      */
39408     getActivePanel : function(){
39409         return this.activePanel;
39410     },
39411
39412     validateVisibility : function(){
39413         if(this.panels.getCount() < 1){
39414             this.updateTitle("&#160;");
39415             this.closeBtn.hide();
39416             this.hide();
39417         }else{
39418             if(!this.isVisible()){
39419                 this.show();
39420             }
39421         }
39422     },
39423
39424     /**
39425      * Adds the passed ContentPanel(s) to this region.
39426      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39427      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39428      */
39429     add : function(panel)
39430     {
39431         if(arguments.length > 1){
39432             for(var i = 0, len = arguments.length; i < len; i++) {
39433                 this.add(arguments[i]);
39434             }
39435             return null;
39436         }
39437         
39438         // if we have not been rendered yet, then we can not really do much of this..
39439         if (!this.bodyEl) {
39440             this.unrendered_panels.push(panel);
39441             return panel;
39442         }
39443         
39444         
39445         
39446         
39447         if(this.hasPanel(panel)){
39448             this.showPanel(panel);
39449             return panel;
39450         }
39451         panel.setRegion(this);
39452         this.panels.add(panel);
39453        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39454             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39455             // and hide them... ???
39456             this.bodyEl.dom.appendChild(panel.getEl().dom);
39457             if(panel.background !== true){
39458                 this.setActivePanel(panel);
39459             }
39460             this.fireEvent("paneladded", this, panel);
39461             return panel;
39462         }
39463         */
39464         if(!this.tabs){
39465             this.initTabs();
39466         }else{
39467             this.initPanelAsTab(panel);
39468         }
39469         
39470         
39471         if(panel.background !== true){
39472             this.tabs.activate(panel.getEl().id);
39473         }
39474         this.fireEvent("paneladded", this, panel);
39475         return panel;
39476     },
39477
39478     /**
39479      * Hides the tab for the specified panel.
39480      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39481      */
39482     hidePanel : function(panel){
39483         if(this.tabs && (panel = this.getPanel(panel))){
39484             this.tabs.hideTab(panel.getEl().id);
39485         }
39486     },
39487
39488     /**
39489      * Unhides the tab for a previously hidden panel.
39490      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39491      */
39492     unhidePanel : function(panel){
39493         if(this.tabs && (panel = this.getPanel(panel))){
39494             this.tabs.unhideTab(panel.getEl().id);
39495         }
39496     },
39497
39498     clearPanels : function(){
39499         while(this.panels.getCount() > 0){
39500              this.remove(this.panels.first());
39501         }
39502     },
39503
39504     /**
39505      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39506      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39507      * @param {Boolean} preservePanel Overrides the config preservePanel option
39508      * @return {Roo.ContentPanel} The panel that was removed
39509      */
39510     remove : function(panel, preservePanel)
39511     {
39512         panel = this.getPanel(panel);
39513         if(!panel){
39514             return null;
39515         }
39516         var e = {};
39517         this.fireEvent("beforeremove", this, panel, e);
39518         if(e.cancel === true){
39519             return null;
39520         }
39521         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39522         var panelId = panel.getId();
39523         this.panels.removeKey(panelId);
39524         if(preservePanel){
39525             document.body.appendChild(panel.getEl().dom);
39526         }
39527         if(this.tabs){
39528             this.tabs.removeTab(panel.getEl().id);
39529         }else if (!preservePanel){
39530             this.bodyEl.dom.removeChild(panel.getEl().dom);
39531         }
39532         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39533             var p = this.panels.first();
39534             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39535             tempEl.appendChild(p.getEl().dom);
39536             this.bodyEl.update("");
39537             this.bodyEl.dom.appendChild(p.getEl().dom);
39538             tempEl = null;
39539             this.updateTitle(p.getTitle());
39540             this.tabs = null;
39541             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39542             this.setActivePanel(p);
39543         }
39544         panel.setRegion(null);
39545         if(this.activePanel == panel){
39546             this.activePanel = null;
39547         }
39548         if(this.config.autoDestroy !== false && preservePanel !== true){
39549             try{panel.destroy();}catch(e){}
39550         }
39551         this.fireEvent("panelremoved", this, panel);
39552         return panel;
39553     },
39554
39555     /**
39556      * Returns the TabPanel component used by this region
39557      * @return {Roo.TabPanel}
39558      */
39559     getTabs : function(){
39560         return this.tabs;
39561     },
39562
39563     createTool : function(parentEl, className){
39564         var btn = Roo.DomHelper.append(parentEl, {
39565             tag: "div",
39566             cls: "x-layout-tools-button",
39567             children: [ {
39568                 tag: "div",
39569                 cls: "roo-layout-tools-button-inner " + className,
39570                 html: "&#160;"
39571             }]
39572         }, true);
39573         btn.addClassOnOver("roo-layout-tools-button-over");
39574         return btn;
39575     }
39576 });/*
39577  * Based on:
39578  * Ext JS Library 1.1.1
39579  * Copyright(c) 2006-2007, Ext JS, LLC.
39580  *
39581  * Originally Released Under LGPL - original licence link has changed is not relivant.
39582  *
39583  * Fork - LGPL
39584  * <script type="text/javascript">
39585  */
39586  
39587
39588
39589 /**
39590  * @class Roo.SplitLayoutRegion
39591  * @extends Roo.LayoutRegion
39592  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39593  */
39594 Roo.bootstrap.layout.Split = function(config){
39595     this.cursor = config.cursor;
39596     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39597 };
39598
39599 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39600 {
39601     splitTip : "Drag to resize.",
39602     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39603     useSplitTips : false,
39604
39605     applyConfig : function(config){
39606         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39607     },
39608     
39609     onRender : function(ctr,pos) {
39610         
39611         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39612         if(!this.config.split){
39613             return;
39614         }
39615         if(!this.split){
39616             
39617             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39618                             tag: "div",
39619                             id: this.el.id + "-split",
39620                             cls: "roo-layout-split roo-layout-split-"+this.position,
39621                             html: "&#160;"
39622             });
39623             /** The SplitBar for this region 
39624             * @type Roo.SplitBar */
39625             // does not exist yet...
39626             Roo.log([this.position, this.orientation]);
39627             
39628             this.split = new Roo.bootstrap.SplitBar({
39629                 dragElement : splitEl,
39630                 resizingElement: this.el,
39631                 orientation : this.orientation
39632             });
39633             
39634             this.split.on("moved", this.onSplitMove, this);
39635             this.split.useShim = this.config.useShim === true;
39636             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39637             if(this.useSplitTips){
39638                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39639             }
39640             //if(config.collapsible){
39641             //    this.split.el.on("dblclick", this.collapse,  this);
39642             //}
39643         }
39644         if(typeof this.config.minSize != "undefined"){
39645             this.split.minSize = this.config.minSize;
39646         }
39647         if(typeof this.config.maxSize != "undefined"){
39648             this.split.maxSize = this.config.maxSize;
39649         }
39650         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39651             this.hideSplitter();
39652         }
39653         
39654     },
39655
39656     getHMaxSize : function(){
39657          var cmax = this.config.maxSize || 10000;
39658          var center = this.mgr.getRegion("center");
39659          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39660     },
39661
39662     getVMaxSize : function(){
39663          var cmax = this.config.maxSize || 10000;
39664          var center = this.mgr.getRegion("center");
39665          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39666     },
39667
39668     onSplitMove : function(split, newSize){
39669         this.fireEvent("resized", this, newSize);
39670     },
39671     
39672     /** 
39673      * Returns the {@link Roo.SplitBar} for this region.
39674      * @return {Roo.SplitBar}
39675      */
39676     getSplitBar : function(){
39677         return this.split;
39678     },
39679     
39680     hide : function(){
39681         this.hideSplitter();
39682         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39683     },
39684
39685     hideSplitter : function(){
39686         if(this.split){
39687             this.split.el.setLocation(-2000,-2000);
39688             this.split.el.hide();
39689         }
39690     },
39691
39692     show : function(){
39693         if(this.split){
39694             this.split.el.show();
39695         }
39696         Roo.bootstrap.layout.Split.superclass.show.call(this);
39697     },
39698     
39699     beforeSlide: function(){
39700         if(Roo.isGecko){// firefox overflow auto bug workaround
39701             this.bodyEl.clip();
39702             if(this.tabs) {
39703                 this.tabs.bodyEl.clip();
39704             }
39705             if(this.activePanel){
39706                 this.activePanel.getEl().clip();
39707                 
39708                 if(this.activePanel.beforeSlide){
39709                     this.activePanel.beforeSlide();
39710                 }
39711             }
39712         }
39713     },
39714     
39715     afterSlide : function(){
39716         if(Roo.isGecko){// firefox overflow auto bug workaround
39717             this.bodyEl.unclip();
39718             if(this.tabs) {
39719                 this.tabs.bodyEl.unclip();
39720             }
39721             if(this.activePanel){
39722                 this.activePanel.getEl().unclip();
39723                 if(this.activePanel.afterSlide){
39724                     this.activePanel.afterSlide();
39725                 }
39726             }
39727         }
39728     },
39729
39730     initAutoHide : function(){
39731         if(this.autoHide !== false){
39732             if(!this.autoHideHd){
39733                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39734                 this.autoHideHd = {
39735                     "mouseout": function(e){
39736                         if(!e.within(this.el, true)){
39737                             st.delay(500);
39738                         }
39739                     },
39740                     "mouseover" : function(e){
39741                         st.cancel();
39742                     },
39743                     scope : this
39744                 };
39745             }
39746             this.el.on(this.autoHideHd);
39747         }
39748     },
39749
39750     clearAutoHide : function(){
39751         if(this.autoHide !== false){
39752             this.el.un("mouseout", this.autoHideHd.mouseout);
39753             this.el.un("mouseover", this.autoHideHd.mouseover);
39754         }
39755     },
39756
39757     clearMonitor : function(){
39758         Roo.get(document).un("click", this.slideInIf, this);
39759     },
39760
39761     // these names are backwards but not changed for compat
39762     slideOut : function(){
39763         if(this.isSlid || this.el.hasActiveFx()){
39764             return;
39765         }
39766         this.isSlid = true;
39767         if(this.collapseBtn){
39768             this.collapseBtn.hide();
39769         }
39770         this.closeBtnState = this.closeBtn.getStyle('display');
39771         this.closeBtn.hide();
39772         if(this.stickBtn){
39773             this.stickBtn.show();
39774         }
39775         this.el.show();
39776         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39777         this.beforeSlide();
39778         this.el.setStyle("z-index", 10001);
39779         this.el.slideIn(this.getSlideAnchor(), {
39780             callback: function(){
39781                 this.afterSlide();
39782                 this.initAutoHide();
39783                 Roo.get(document).on("click", this.slideInIf, this);
39784                 this.fireEvent("slideshow", this);
39785             },
39786             scope: this,
39787             block: true
39788         });
39789     },
39790
39791     afterSlideIn : function(){
39792         this.clearAutoHide();
39793         this.isSlid = false;
39794         this.clearMonitor();
39795         this.el.setStyle("z-index", "");
39796         if(this.collapseBtn){
39797             this.collapseBtn.show();
39798         }
39799         this.closeBtn.setStyle('display', this.closeBtnState);
39800         if(this.stickBtn){
39801             this.stickBtn.hide();
39802         }
39803         this.fireEvent("slidehide", this);
39804     },
39805
39806     slideIn : function(cb){
39807         if(!this.isSlid || this.el.hasActiveFx()){
39808             Roo.callback(cb);
39809             return;
39810         }
39811         this.isSlid = false;
39812         this.beforeSlide();
39813         this.el.slideOut(this.getSlideAnchor(), {
39814             callback: function(){
39815                 this.el.setLeftTop(-10000, -10000);
39816                 this.afterSlide();
39817                 this.afterSlideIn();
39818                 Roo.callback(cb);
39819             },
39820             scope: this,
39821             block: true
39822         });
39823     },
39824     
39825     slideInIf : function(e){
39826         if(!e.within(this.el)){
39827             this.slideIn();
39828         }
39829     },
39830
39831     animateCollapse : function(){
39832         this.beforeSlide();
39833         this.el.setStyle("z-index", 20000);
39834         var anchor = this.getSlideAnchor();
39835         this.el.slideOut(anchor, {
39836             callback : function(){
39837                 this.el.setStyle("z-index", "");
39838                 this.collapsedEl.slideIn(anchor, {duration:.3});
39839                 this.afterSlide();
39840                 this.el.setLocation(-10000,-10000);
39841                 this.el.hide();
39842                 this.fireEvent("collapsed", this);
39843             },
39844             scope: this,
39845             block: true
39846         });
39847     },
39848
39849     animateExpand : function(){
39850         this.beforeSlide();
39851         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39852         this.el.setStyle("z-index", 20000);
39853         this.collapsedEl.hide({
39854             duration:.1
39855         });
39856         this.el.slideIn(this.getSlideAnchor(), {
39857             callback : function(){
39858                 this.el.setStyle("z-index", "");
39859                 this.afterSlide();
39860                 if(this.split){
39861                     this.split.el.show();
39862                 }
39863                 this.fireEvent("invalidated", this);
39864                 this.fireEvent("expanded", this);
39865             },
39866             scope: this,
39867             block: true
39868         });
39869     },
39870
39871     anchors : {
39872         "west" : "left",
39873         "east" : "right",
39874         "north" : "top",
39875         "south" : "bottom"
39876     },
39877
39878     sanchors : {
39879         "west" : "l",
39880         "east" : "r",
39881         "north" : "t",
39882         "south" : "b"
39883     },
39884
39885     canchors : {
39886         "west" : "tl-tr",
39887         "east" : "tr-tl",
39888         "north" : "tl-bl",
39889         "south" : "bl-tl"
39890     },
39891
39892     getAnchor : function(){
39893         return this.anchors[this.position];
39894     },
39895
39896     getCollapseAnchor : function(){
39897         return this.canchors[this.position];
39898     },
39899
39900     getSlideAnchor : function(){
39901         return this.sanchors[this.position];
39902     },
39903
39904     getAlignAdj : function(){
39905         var cm = this.cmargins;
39906         switch(this.position){
39907             case "west":
39908                 return [0, 0];
39909             break;
39910             case "east":
39911                 return [0, 0];
39912             break;
39913             case "north":
39914                 return [0, 0];
39915             break;
39916             case "south":
39917                 return [0, 0];
39918             break;
39919         }
39920     },
39921
39922     getExpandAdj : function(){
39923         var c = this.collapsedEl, cm = this.cmargins;
39924         switch(this.position){
39925             case "west":
39926                 return [-(cm.right+c.getWidth()+cm.left), 0];
39927             break;
39928             case "east":
39929                 return [cm.right+c.getWidth()+cm.left, 0];
39930             break;
39931             case "north":
39932                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39933             break;
39934             case "south":
39935                 return [0, cm.top+cm.bottom+c.getHeight()];
39936             break;
39937         }
39938     }
39939 });/*
39940  * Based on:
39941  * Ext JS Library 1.1.1
39942  * Copyright(c) 2006-2007, Ext JS, LLC.
39943  *
39944  * Originally Released Under LGPL - original licence link has changed is not relivant.
39945  *
39946  * Fork - LGPL
39947  * <script type="text/javascript">
39948  */
39949 /*
39950  * These classes are private internal classes
39951  */
39952 Roo.bootstrap.layout.Center = function(config){
39953     config.region = "center";
39954     Roo.bootstrap.layout.Region.call(this, config);
39955     this.visible = true;
39956     this.minWidth = config.minWidth || 20;
39957     this.minHeight = config.minHeight || 20;
39958 };
39959
39960 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39961     hide : function(){
39962         // center panel can't be hidden
39963     },
39964     
39965     show : function(){
39966         // center panel can't be hidden
39967     },
39968     
39969     getMinWidth: function(){
39970         return this.minWidth;
39971     },
39972     
39973     getMinHeight: function(){
39974         return this.minHeight;
39975     }
39976 });
39977
39978
39979
39980
39981  
39982
39983
39984
39985
39986
39987
39988 Roo.bootstrap.layout.North = function(config)
39989 {
39990     config.region = 'north';
39991     config.cursor = 'n-resize';
39992     
39993     Roo.bootstrap.layout.Split.call(this, config);
39994     
39995     
39996     if(this.split){
39997         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39998         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39999         this.split.el.addClass("roo-layout-split-v");
40000     }
40001     //var size = config.initialSize || config.height;
40002     //if(this.el && typeof size != "undefined"){
40003     //    this.el.setHeight(size);
40004     //}
40005 };
40006 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40007 {
40008     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40009      
40010      
40011     onRender : function(ctr, pos)
40012     {
40013         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40014         var size = this.config.initialSize || this.config.height;
40015         if(this.el && typeof size != "undefined"){
40016             this.el.setHeight(size);
40017         }
40018     
40019     },
40020     
40021     getBox : function(){
40022         if(this.collapsed){
40023             return this.collapsedEl.getBox();
40024         }
40025         var box = this.el.getBox();
40026         if(this.split){
40027             box.height += this.split.el.getHeight();
40028         }
40029         return box;
40030     },
40031     
40032     updateBox : function(box){
40033         if(this.split && !this.collapsed){
40034             box.height -= this.split.el.getHeight();
40035             this.split.el.setLeft(box.x);
40036             this.split.el.setTop(box.y+box.height);
40037             this.split.el.setWidth(box.width);
40038         }
40039         if(this.collapsed){
40040             this.updateBody(box.width, null);
40041         }
40042         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40043     }
40044 });
40045
40046
40047
40048
40049
40050 Roo.bootstrap.layout.South = function(config){
40051     config.region = 'south';
40052     config.cursor = 's-resize';
40053     Roo.bootstrap.layout.Split.call(this, config);
40054     if(this.split){
40055         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40056         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40057         this.split.el.addClass("roo-layout-split-v");
40058     }
40059     
40060 };
40061
40062 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40063     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40064     
40065     onRender : function(ctr, pos)
40066     {
40067         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40068         var size = this.config.initialSize || this.config.height;
40069         if(this.el && typeof size != "undefined"){
40070             this.el.setHeight(size);
40071         }
40072     
40073     },
40074     
40075     getBox : function(){
40076         if(this.collapsed){
40077             return this.collapsedEl.getBox();
40078         }
40079         var box = this.el.getBox();
40080         if(this.split){
40081             var sh = this.split.el.getHeight();
40082             box.height += sh;
40083             box.y -= sh;
40084         }
40085         return box;
40086     },
40087     
40088     updateBox : function(box){
40089         if(this.split && !this.collapsed){
40090             var sh = this.split.el.getHeight();
40091             box.height -= sh;
40092             box.y += sh;
40093             this.split.el.setLeft(box.x);
40094             this.split.el.setTop(box.y-sh);
40095             this.split.el.setWidth(box.width);
40096         }
40097         if(this.collapsed){
40098             this.updateBody(box.width, null);
40099         }
40100         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40101     }
40102 });
40103
40104 Roo.bootstrap.layout.East = function(config){
40105     config.region = "east";
40106     config.cursor = "e-resize";
40107     Roo.bootstrap.layout.Split.call(this, config);
40108     if(this.split){
40109         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40110         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40111         this.split.el.addClass("roo-layout-split-h");
40112     }
40113     
40114 };
40115 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40116     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40117     
40118     onRender : function(ctr, pos)
40119     {
40120         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40121         var size = this.config.initialSize || this.config.width;
40122         if(this.el && typeof size != "undefined"){
40123             this.el.setWidth(size);
40124         }
40125     
40126     },
40127     
40128     getBox : function(){
40129         if(this.collapsed){
40130             return this.collapsedEl.getBox();
40131         }
40132         var box = this.el.getBox();
40133         if(this.split){
40134             var sw = this.split.el.getWidth();
40135             box.width += sw;
40136             box.x -= sw;
40137         }
40138         return box;
40139     },
40140
40141     updateBox : function(box){
40142         if(this.split && !this.collapsed){
40143             var sw = this.split.el.getWidth();
40144             box.width -= sw;
40145             this.split.el.setLeft(box.x);
40146             this.split.el.setTop(box.y);
40147             this.split.el.setHeight(box.height);
40148             box.x += sw;
40149         }
40150         if(this.collapsed){
40151             this.updateBody(null, box.height);
40152         }
40153         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40154     }
40155 });
40156
40157 Roo.bootstrap.layout.West = function(config){
40158     config.region = "west";
40159     config.cursor = "w-resize";
40160     
40161     Roo.bootstrap.layout.Split.call(this, config);
40162     if(this.split){
40163         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40164         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40165         this.split.el.addClass("roo-layout-split-h");
40166     }
40167     
40168 };
40169 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40170     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40171     
40172     onRender: function(ctr, pos)
40173     {
40174         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40175         var size = this.config.initialSize || this.config.width;
40176         if(typeof size != "undefined"){
40177             this.el.setWidth(size);
40178         }
40179     },
40180     
40181     getBox : function(){
40182         if(this.collapsed){
40183             return this.collapsedEl.getBox();
40184         }
40185         var box = this.el.getBox();
40186         if (box.width == 0) {
40187             box.width = this.config.width; // kludge?
40188         }
40189         if(this.split){
40190             box.width += this.split.el.getWidth();
40191         }
40192         return box;
40193     },
40194     
40195     updateBox : function(box){
40196         if(this.split && !this.collapsed){
40197             var sw = this.split.el.getWidth();
40198             box.width -= sw;
40199             this.split.el.setLeft(box.x+box.width);
40200             this.split.el.setTop(box.y);
40201             this.split.el.setHeight(box.height);
40202         }
40203         if(this.collapsed){
40204             this.updateBody(null, box.height);
40205         }
40206         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40207     }
40208 });Roo.namespace("Roo.bootstrap.panel");/*
40209  * Based on:
40210  * Ext JS Library 1.1.1
40211  * Copyright(c) 2006-2007, Ext JS, LLC.
40212  *
40213  * Originally Released Under LGPL - original licence link has changed is not relivant.
40214  *
40215  * Fork - LGPL
40216  * <script type="text/javascript">
40217  */
40218 /**
40219  * @class Roo.bootstrap.paenl.Content
40220  * @extends Roo.util.Observable
40221  * @builder-top
40222  * @children Roo.bootstrap.Component
40223  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40224  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40225  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40226  * @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
40227  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40228  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40229  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40230  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40231  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40232  * @cfg {String} title          The title for this panel
40233  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40234  * @cfg {String} url            Calls {@link #setUrl} with this value
40235  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40236  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40237  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40238  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40239  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40240  * @cfg {Boolean} badges render the badges
40241  * @cfg {String} cls  extra classes to use  
40242  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40243  
40244  * @constructor
40245  * Create a new ContentPanel.
40246  * @param {String/Object} config A string to set only the title or a config object
40247  
40248  */
40249 Roo.bootstrap.panel.Content = function( config){
40250     
40251     this.tpl = config.tpl || false;
40252     
40253     var el = config.el;
40254     var content = config.content;
40255
40256     if(config.autoCreate){ // xtype is available if this is called from factory
40257         el = Roo.id();
40258     }
40259     this.el = Roo.get(el);
40260     if(!this.el && config && config.autoCreate){
40261         if(typeof config.autoCreate == "object"){
40262             if(!config.autoCreate.id){
40263                 config.autoCreate.id = config.id||el;
40264             }
40265             this.el = Roo.DomHelper.append(document.body,
40266                         config.autoCreate, true);
40267         }else{
40268             var elcfg =  {
40269                 tag: "div",
40270                 cls: (config.cls || '') +
40271                     (config.background ? ' bg-' + config.background : '') +
40272                     " roo-layout-inactive-content",
40273                 id: config.id||el
40274             };
40275             if (config.iframe) {
40276                 elcfg.cn = [
40277                     {
40278                         tag : 'iframe',
40279                         style : 'border: 0px',
40280                         src : 'about:blank'
40281                     }
40282                 ];
40283             }
40284               
40285             if (config.html) {
40286                 elcfg.html = config.html;
40287                 
40288             }
40289                         
40290             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40291             if (config.iframe) {
40292                 this.iframeEl = this.el.select('iframe',true).first();
40293             }
40294             
40295         }
40296     } 
40297     this.closable = false;
40298     this.loaded = false;
40299     this.active = false;
40300    
40301       
40302     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40303         
40304         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40305         
40306         this.wrapEl = this.el; //this.el.wrap();
40307         var ti = [];
40308         if (config.toolbar.items) {
40309             ti = config.toolbar.items ;
40310             delete config.toolbar.items ;
40311         }
40312         
40313         var nitems = [];
40314         this.toolbar.render(this.wrapEl, 'before');
40315         for(var i =0;i < ti.length;i++) {
40316           //  Roo.log(['add child', items[i]]);
40317             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40318         }
40319         this.toolbar.items = nitems;
40320         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40321         delete config.toolbar;
40322         
40323     }
40324     /*
40325     // xtype created footer. - not sure if will work as we normally have to render first..
40326     if (this.footer && !this.footer.el && this.footer.xtype) {
40327         if (!this.wrapEl) {
40328             this.wrapEl = this.el.wrap();
40329         }
40330     
40331         this.footer.container = this.wrapEl.createChild();
40332          
40333         this.footer = Roo.factory(this.footer, Roo);
40334         
40335     }
40336     */
40337     
40338      if(typeof config == "string"){
40339         this.title = config;
40340     }else{
40341         Roo.apply(this, config);
40342     }
40343     
40344     if(this.resizeEl){
40345         this.resizeEl = Roo.get(this.resizeEl, true);
40346     }else{
40347         this.resizeEl = this.el;
40348     }
40349     // handle view.xtype
40350     
40351  
40352     
40353     
40354     this.addEvents({
40355         /**
40356          * @event activate
40357          * Fires when this panel is activated. 
40358          * @param {Roo.ContentPanel} this
40359          */
40360         "activate" : true,
40361         /**
40362          * @event deactivate
40363          * Fires when this panel is activated. 
40364          * @param {Roo.ContentPanel} this
40365          */
40366         "deactivate" : true,
40367
40368         /**
40369          * @event resize
40370          * Fires when this panel is resized if fitToFrame is true.
40371          * @param {Roo.ContentPanel} this
40372          * @param {Number} width The width after any component adjustments
40373          * @param {Number} height The height after any component adjustments
40374          */
40375         "resize" : true,
40376         
40377          /**
40378          * @event render
40379          * Fires when this tab is created
40380          * @param {Roo.ContentPanel} this
40381          */
40382         "render" : true,
40383         
40384           /**
40385          * @event scroll
40386          * Fires when this content is scrolled
40387          * @param {Roo.ContentPanel} this
40388          * @param {Event} scrollEvent
40389          */
40390         "scroll" : true
40391         
40392         
40393         
40394     });
40395     
40396
40397     
40398     
40399     if(this.autoScroll && !this.iframe){
40400         this.resizeEl.setStyle("overflow", "auto");
40401         this.resizeEl.on('scroll', this.onScroll, this);
40402     } else {
40403         // fix randome scrolling
40404         //this.el.on('scroll', function() {
40405         //    Roo.log('fix random scolling');
40406         //    this.scrollTo('top',0); 
40407         //});
40408     }
40409     content = content || this.content;
40410     if(content){
40411         this.setContent(content);
40412     }
40413     if(config && config.url){
40414         this.setUrl(this.url, this.params, this.loadOnce);
40415     }
40416     
40417     
40418     
40419     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40420     
40421     if (this.view && typeof(this.view.xtype) != 'undefined') {
40422         this.view.el = this.el.appendChild(document.createElement("div"));
40423         this.view = Roo.factory(this.view); 
40424         this.view.render  &&  this.view.render(false, '');  
40425     }
40426     
40427     
40428     this.fireEvent('render', this);
40429 };
40430
40431 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40432     
40433     cls : '',
40434     background : '',
40435     
40436     tabTip : '',
40437     
40438     iframe : false,
40439     iframeEl : false,
40440     
40441     /* Resize Element - use this to work out scroll etc. */
40442     resizeEl : false,
40443     
40444     setRegion : function(region){
40445         this.region = region;
40446         this.setActiveClass(region && !this.background);
40447     },
40448     
40449     
40450     setActiveClass: function(state)
40451     {
40452         if(state){
40453            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40454            this.el.setStyle('position','relative');
40455         }else{
40456            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40457            this.el.setStyle('position', 'absolute');
40458         } 
40459     },
40460     
40461     /**
40462      * Returns the toolbar for this Panel if one was configured. 
40463      * @return {Roo.Toolbar} 
40464      */
40465     getToolbar : function(){
40466         return this.toolbar;
40467     },
40468     
40469     setActiveState : function(active)
40470     {
40471         this.active = active;
40472         this.setActiveClass(active);
40473         if(!active){
40474             if(this.fireEvent("deactivate", this) === false){
40475                 return false;
40476             }
40477             return true;
40478         }
40479         this.fireEvent("activate", this);
40480         return true;
40481     },
40482     /**
40483      * Updates this panel's element (not for iframe)
40484      * @param {String} content The new content
40485      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40486     */
40487     setContent : function(content, loadScripts){
40488         if (this.iframe) {
40489             return;
40490         }
40491         
40492         this.el.update(content, loadScripts);
40493     },
40494
40495     ignoreResize : function(w, h){
40496         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40497             return true;
40498         }else{
40499             this.lastSize = {width: w, height: h};
40500             return false;
40501         }
40502     },
40503     /**
40504      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40505      * @return {Roo.UpdateManager} The UpdateManager
40506      */
40507     getUpdateManager : function(){
40508         if (this.iframe) {
40509             return false;
40510         }
40511         return this.el.getUpdateManager();
40512     },
40513      /**
40514      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40515      * Does not work with IFRAME contents
40516      * @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:
40517 <pre><code>
40518 panel.load({
40519     url: "your-url.php",
40520     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40521     callback: yourFunction,
40522     scope: yourObject, //(optional scope)
40523     discardUrl: false,
40524     nocache: false,
40525     text: "Loading...",
40526     timeout: 30,
40527     scripts: false
40528 });
40529 </code></pre>
40530      
40531      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40532      * 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.
40533      * @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}
40534      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40535      * @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.
40536      * @return {Roo.ContentPanel} this
40537      */
40538     load : function(){
40539         
40540         if (this.iframe) {
40541             return this;
40542         }
40543         
40544         var um = this.el.getUpdateManager();
40545         um.update.apply(um, arguments);
40546         return this;
40547     },
40548
40549
40550     /**
40551      * 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.
40552      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40553      * @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)
40554      * @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)
40555      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40556      */
40557     setUrl : function(url, params, loadOnce){
40558         if (this.iframe) {
40559             this.iframeEl.dom.src = url;
40560             return false;
40561         }
40562         
40563         if(this.refreshDelegate){
40564             this.removeListener("activate", this.refreshDelegate);
40565         }
40566         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40567         this.on("activate", this.refreshDelegate);
40568         return this.el.getUpdateManager();
40569     },
40570     
40571     _handleRefresh : function(url, params, loadOnce){
40572         if(!loadOnce || !this.loaded){
40573             var updater = this.el.getUpdateManager();
40574             updater.update(url, params, this._setLoaded.createDelegate(this));
40575         }
40576     },
40577     
40578     _setLoaded : function(){
40579         this.loaded = true;
40580     }, 
40581     
40582     /**
40583      * Returns this panel's id
40584      * @return {String} 
40585      */
40586     getId : function(){
40587         return this.el.id;
40588     },
40589     
40590     /** 
40591      * Returns this panel's element - used by regiosn to add.
40592      * @return {Roo.Element} 
40593      */
40594     getEl : function(){
40595         return this.wrapEl || this.el;
40596     },
40597     
40598    
40599     
40600     adjustForComponents : function(width, height)
40601     {
40602         //Roo.log('adjustForComponents ');
40603         if(this.resizeEl != this.el){
40604             width -= this.el.getFrameWidth('lr');
40605             height -= this.el.getFrameWidth('tb');
40606         }
40607         if(this.toolbar){
40608             var te = this.toolbar.getEl();
40609             te.setWidth(width);
40610             height -= te.getHeight();
40611         }
40612         if(this.footer){
40613             var te = this.footer.getEl();
40614             te.setWidth(width);
40615             height -= te.getHeight();
40616         }
40617         
40618         
40619         if(this.adjustments){
40620             width += this.adjustments[0];
40621             height += this.adjustments[1];
40622         }
40623         return {"width": width, "height": height};
40624     },
40625     
40626     setSize : function(width, height){
40627         if(this.fitToFrame && !this.ignoreResize(width, height)){
40628             if(this.fitContainer && this.resizeEl != this.el){
40629                 this.el.setSize(width, height);
40630             }
40631             var size = this.adjustForComponents(width, height);
40632             if (this.iframe) {
40633                 this.iframeEl.setSize(width,height);
40634             }
40635             
40636             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40637             this.fireEvent('resize', this, size.width, size.height);
40638             
40639             
40640         }
40641     },
40642     
40643     /**
40644      * Returns this panel's title
40645      * @return {String} 
40646      */
40647     getTitle : function(){
40648         
40649         if (typeof(this.title) != 'object') {
40650             return this.title;
40651         }
40652         
40653         var t = '';
40654         for (var k in this.title) {
40655             if (!this.title.hasOwnProperty(k)) {
40656                 continue;
40657             }
40658             
40659             if (k.indexOf('-') >= 0) {
40660                 var s = k.split('-');
40661                 for (var i = 0; i<s.length; i++) {
40662                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40663                 }
40664             } else {
40665                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40666             }
40667         }
40668         return t;
40669     },
40670     
40671     /**
40672      * Set this panel's title
40673      * @param {String} title
40674      */
40675     setTitle : function(title){
40676         this.title = title;
40677         if(this.region){
40678             this.region.updatePanelTitle(this, title);
40679         }
40680     },
40681     
40682     /**
40683      * Returns true is this panel was configured to be closable
40684      * @return {Boolean} 
40685      */
40686     isClosable : function(){
40687         return this.closable;
40688     },
40689     
40690     beforeSlide : function(){
40691         this.el.clip();
40692         this.resizeEl.clip();
40693     },
40694     
40695     afterSlide : function(){
40696         this.el.unclip();
40697         this.resizeEl.unclip();
40698     },
40699     
40700     /**
40701      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40702      *   Will fail silently if the {@link #setUrl} method has not been called.
40703      *   This does not activate the panel, just updates its content.
40704      */
40705     refresh : function(){
40706         if(this.refreshDelegate){
40707            this.loaded = false;
40708            this.refreshDelegate();
40709         }
40710     },
40711     
40712     /**
40713      * Destroys this panel
40714      */
40715     destroy : function(){
40716         this.el.removeAllListeners();
40717         var tempEl = document.createElement("span");
40718         tempEl.appendChild(this.el.dom);
40719         tempEl.innerHTML = "";
40720         this.el.remove();
40721         this.el = null;
40722     },
40723     
40724     /**
40725      * form - if the content panel contains a form - this is a reference to it.
40726      * @type {Roo.form.Form}
40727      */
40728     form : false,
40729     /**
40730      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40731      *    This contains a reference to it.
40732      * @type {Roo.View}
40733      */
40734     view : false,
40735     
40736       /**
40737      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40738      * <pre><code>
40739
40740 layout.addxtype({
40741        xtype : 'Form',
40742        items: [ .... ]
40743    }
40744 );
40745
40746 </code></pre>
40747      * @param {Object} cfg Xtype definition of item to add.
40748      */
40749     
40750     
40751     getChildContainer: function () {
40752         return this.getEl();
40753     },
40754     
40755     
40756     onScroll : function(e)
40757     {
40758         this.fireEvent('scroll', this, e);
40759     }
40760     
40761     
40762     /*
40763         var  ret = new Roo.factory(cfg);
40764         return ret;
40765         
40766         
40767         // add form..
40768         if (cfg.xtype.match(/^Form$/)) {
40769             
40770             var el;
40771             //if (this.footer) {
40772             //    el = this.footer.container.insertSibling(false, 'before');
40773             //} else {
40774                 el = this.el.createChild();
40775             //}
40776
40777             this.form = new  Roo.form.Form(cfg);
40778             
40779             
40780             if ( this.form.allItems.length) {
40781                 this.form.render(el.dom);
40782             }
40783             return this.form;
40784         }
40785         // should only have one of theses..
40786         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40787             // views.. should not be just added - used named prop 'view''
40788             
40789             cfg.el = this.el.appendChild(document.createElement("div"));
40790             // factory?
40791             
40792             var ret = new Roo.factory(cfg);
40793              
40794              ret.render && ret.render(false, ''); // render blank..
40795             this.view = ret;
40796             return ret;
40797         }
40798         return false;
40799     }
40800     \*/
40801 });
40802  
40803 /**
40804  * @class Roo.bootstrap.panel.Grid
40805  * @extends Roo.bootstrap.panel.Content
40806  * @constructor
40807  * Create a new GridPanel.
40808  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40809  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40810  * @param {Object} config A the config object
40811   
40812  */
40813
40814
40815
40816 Roo.bootstrap.panel.Grid = function(config)
40817 {
40818     
40819       
40820     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40821         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40822
40823     config.el = this.wrapper;
40824     //this.el = this.wrapper;
40825     
40826       if (config.container) {
40827         // ctor'ed from a Border/panel.grid
40828         
40829         
40830         this.wrapper.setStyle("overflow", "hidden");
40831         this.wrapper.addClass('roo-grid-container');
40832
40833     }
40834     
40835     
40836     if(config.toolbar){
40837         var tool_el = this.wrapper.createChild();    
40838         this.toolbar = Roo.factory(config.toolbar);
40839         var ti = [];
40840         if (config.toolbar.items) {
40841             ti = config.toolbar.items ;
40842             delete config.toolbar.items ;
40843         }
40844         
40845         var nitems = [];
40846         this.toolbar.render(tool_el);
40847         for(var i =0;i < ti.length;i++) {
40848           //  Roo.log(['add child', items[i]]);
40849             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40850         }
40851         this.toolbar.items = nitems;
40852         
40853         delete config.toolbar;
40854     }
40855     
40856     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40857     config.grid.scrollBody = true;;
40858     config.grid.monitorWindowResize = false; // turn off autosizing
40859     config.grid.autoHeight = false;
40860     config.grid.autoWidth = false;
40861     
40862     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40863     
40864     if (config.background) {
40865         // render grid on panel activation (if panel background)
40866         this.on('activate', function(gp) {
40867             if (!gp.grid.rendered) {
40868                 gp.grid.render(this.wrapper);
40869                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40870             }
40871         });
40872             
40873     } else {
40874         this.grid.render(this.wrapper);
40875         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40876
40877     }
40878     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40879     // ??? needed ??? config.el = this.wrapper;
40880     
40881     
40882     
40883   
40884     // xtype created footer. - not sure if will work as we normally have to render first..
40885     if (this.footer && !this.footer.el && this.footer.xtype) {
40886         
40887         var ctr = this.grid.getView().getFooterPanel(true);
40888         this.footer.dataSource = this.grid.dataSource;
40889         this.footer = Roo.factory(this.footer, Roo);
40890         this.footer.render(ctr);
40891         
40892     }
40893     
40894     
40895     
40896     
40897      
40898 };
40899
40900 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40901     getId : function(){
40902         return this.grid.id;
40903     },
40904     
40905     /**
40906      * Returns the grid for this panel
40907      * @return {Roo.bootstrap.Table} 
40908      */
40909     getGrid : function(){
40910         return this.grid;    
40911     },
40912     
40913     setSize : function(width, height){
40914         if(!this.ignoreResize(width, height)){
40915             var grid = this.grid;
40916             var size = this.adjustForComponents(width, height);
40917             // tfoot is not a footer?
40918           
40919             
40920             var gridel = grid.getGridEl();
40921             gridel.setSize(size.width, size.height);
40922             
40923             var tbd = grid.getGridEl().select('tbody', true).first();
40924             var thd = grid.getGridEl().select('thead',true).first();
40925             var tbf= grid.getGridEl().select('tfoot', true).first();
40926
40927             if (tbf) {
40928                 size.height -= tbf.getHeight();
40929             }
40930             if (thd) {
40931                 size.height -= thd.getHeight();
40932             }
40933             
40934             tbd.setSize(size.width, size.height );
40935             // this is for the account management tab -seems to work there.
40936             var thd = grid.getGridEl().select('thead',true).first();
40937             //if (tbd) {
40938             //    tbd.setSize(size.width, size.height - thd.getHeight());
40939             //}
40940              
40941             grid.autoSize();
40942         }
40943     },
40944      
40945     
40946     
40947     beforeSlide : function(){
40948         this.grid.getView().scroller.clip();
40949     },
40950     
40951     afterSlide : function(){
40952         this.grid.getView().scroller.unclip();
40953     },
40954     
40955     destroy : function(){
40956         this.grid.destroy();
40957         delete this.grid;
40958         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40959     }
40960 });
40961
40962 /**
40963  * @class Roo.bootstrap.panel.Nest
40964  * @extends Roo.bootstrap.panel.Content
40965  * @constructor
40966  * Create a new Panel, that can contain a layout.Border.
40967  * 
40968  * 
40969  * @param {String/Object} config A string to set only the title or a config object
40970  */
40971 Roo.bootstrap.panel.Nest = function(config)
40972 {
40973     // construct with only one argument..
40974     /* FIXME - implement nicer consturctors
40975     if (layout.layout) {
40976         config = layout;
40977         layout = config.layout;
40978         delete config.layout;
40979     }
40980     if (layout.xtype && !layout.getEl) {
40981         // then layout needs constructing..
40982         layout = Roo.factory(layout, Roo);
40983     }
40984     */
40985     
40986     config.el =  config.layout.getEl();
40987     
40988     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40989     
40990     config.layout.monitorWindowResize = false; // turn off autosizing
40991     this.layout = config.layout;
40992     this.layout.getEl().addClass("roo-layout-nested-layout");
40993     this.layout.parent = this;
40994     
40995     
40996     
40997     
40998 };
40999
41000 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41001     /**
41002     * @cfg {Roo.BorderLayout} layout The layout for this panel
41003     */
41004     layout : false,
41005
41006     setSize : function(width, height){
41007         if(!this.ignoreResize(width, height)){
41008             var size = this.adjustForComponents(width, height);
41009             var el = this.layout.getEl();
41010             if (size.height < 1) {
41011                 el.setWidth(size.width);   
41012             } else {
41013                 el.setSize(size.width, size.height);
41014             }
41015             var touch = el.dom.offsetWidth;
41016             this.layout.layout();
41017             // ie requires a double layout on the first pass
41018             if(Roo.isIE && !this.initialized){
41019                 this.initialized = true;
41020                 this.layout.layout();
41021             }
41022         }
41023     },
41024     
41025     // activate all subpanels if not currently active..
41026     
41027     setActiveState : function(active){
41028         this.active = active;
41029         this.setActiveClass(active);
41030         
41031         if(!active){
41032             this.fireEvent("deactivate", this);
41033             return;
41034         }
41035         
41036         this.fireEvent("activate", this);
41037         // not sure if this should happen before or after..
41038         if (!this.layout) {
41039             return; // should not happen..
41040         }
41041         var reg = false;
41042         for (var r in this.layout.regions) {
41043             reg = this.layout.getRegion(r);
41044             if (reg.getActivePanel()) {
41045                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41046                 reg.setActivePanel(reg.getActivePanel());
41047                 continue;
41048             }
41049             if (!reg.panels.length) {
41050                 continue;
41051             }
41052             reg.showPanel(reg.getPanel(0));
41053         }
41054         
41055         
41056         
41057         
41058     },
41059     
41060     /**
41061      * Returns the nested BorderLayout for this panel
41062      * @return {Roo.BorderLayout} 
41063      */
41064     getLayout : function(){
41065         return this.layout;
41066     },
41067     
41068      /**
41069      * Adds a xtype elements to the layout of the nested panel
41070      * <pre><code>
41071
41072 panel.addxtype({
41073        xtype : 'ContentPanel',
41074        region: 'west',
41075        items: [ .... ]
41076    }
41077 );
41078
41079 panel.addxtype({
41080         xtype : 'NestedLayoutPanel',
41081         region: 'west',
41082         layout: {
41083            center: { },
41084            west: { }   
41085         },
41086         items : [ ... list of content panels or nested layout panels.. ]
41087    }
41088 );
41089 </code></pre>
41090      * @param {Object} cfg Xtype definition of item to add.
41091      */
41092     addxtype : function(cfg) {
41093         return this.layout.addxtype(cfg);
41094     
41095     }
41096 });/*
41097  * Based on:
41098  * Ext JS Library 1.1.1
41099  * Copyright(c) 2006-2007, Ext JS, LLC.
41100  *
41101  * Originally Released Under LGPL - original licence link has changed is not relivant.
41102  *
41103  * Fork - LGPL
41104  * <script type="text/javascript">
41105  */
41106 /**
41107  * @class Roo.TabPanel
41108  * @extends Roo.util.Observable
41109  * A lightweight tab container.
41110  * <br><br>
41111  * Usage:
41112  * <pre><code>
41113 // basic tabs 1, built from existing content
41114 var tabs = new Roo.TabPanel("tabs1");
41115 tabs.addTab("script", "View Script");
41116 tabs.addTab("markup", "View Markup");
41117 tabs.activate("script");
41118
41119 // more advanced tabs, built from javascript
41120 var jtabs = new Roo.TabPanel("jtabs");
41121 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41122
41123 // set up the UpdateManager
41124 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41125 var updater = tab2.getUpdateManager();
41126 updater.setDefaultUrl("ajax1.htm");
41127 tab2.on('activate', updater.refresh, updater, true);
41128
41129 // Use setUrl for Ajax loading
41130 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41131 tab3.setUrl("ajax2.htm", null, true);
41132
41133 // Disabled tab
41134 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41135 tab4.disable();
41136
41137 jtabs.activate("jtabs-1");
41138  * </code></pre>
41139  * @constructor
41140  * Create a new TabPanel.
41141  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41142  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41143  */
41144 Roo.bootstrap.panel.Tabs = function(config){
41145     /**
41146     * The container element for this TabPanel.
41147     * @type Roo.Element
41148     */
41149     this.el = Roo.get(config.el);
41150     delete config.el;
41151     if(config){
41152         if(typeof config == "boolean"){
41153             this.tabPosition = config ? "bottom" : "top";
41154         }else{
41155             Roo.apply(this, config);
41156         }
41157     }
41158     
41159     if(this.tabPosition == "bottom"){
41160         // if tabs are at the bottom = create the body first.
41161         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41162         this.el.addClass("roo-tabs-bottom");
41163     }
41164     // next create the tabs holders
41165     
41166     if (this.tabPosition == "west"){
41167         
41168         var reg = this.region; // fake it..
41169         while (reg) {
41170             if (!reg.mgr.parent) {
41171                 break;
41172             }
41173             reg = reg.mgr.parent.region;
41174         }
41175         Roo.log("got nest?");
41176         Roo.log(reg);
41177         if (reg.mgr.getRegion('west')) {
41178             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41179             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41180             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41181             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41182             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41183         
41184             
41185         }
41186         
41187         
41188     } else {
41189      
41190         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41191         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41192         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41193         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41194     }
41195     
41196     
41197     if(Roo.isIE){
41198         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41199     }
41200     
41201     // finally - if tabs are at the top, then create the body last..
41202     if(this.tabPosition != "bottom"){
41203         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41204          * @type Roo.Element
41205          */
41206         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41207         this.el.addClass("roo-tabs-top");
41208     }
41209     this.items = [];
41210
41211     this.bodyEl.setStyle("position", "relative");
41212
41213     this.active = null;
41214     this.activateDelegate = this.activate.createDelegate(this);
41215
41216     this.addEvents({
41217         /**
41218          * @event tabchange
41219          * Fires when the active tab changes
41220          * @param {Roo.TabPanel} this
41221          * @param {Roo.TabPanelItem} activePanel The new active tab
41222          */
41223         "tabchange": true,
41224         /**
41225          * @event beforetabchange
41226          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41227          * @param {Roo.TabPanel} this
41228          * @param {Object} e Set cancel to true on this object to cancel the tab change
41229          * @param {Roo.TabPanelItem} tab The tab being changed to
41230          */
41231         "beforetabchange" : true
41232     });
41233
41234     Roo.EventManager.onWindowResize(this.onResize, this);
41235     this.cpad = this.el.getPadding("lr");
41236     this.hiddenCount = 0;
41237
41238
41239     // toolbar on the tabbar support...
41240     if (this.toolbar) {
41241         alert("no toolbar support yet");
41242         this.toolbar  = false;
41243         /*
41244         var tcfg = this.toolbar;
41245         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41246         this.toolbar = new Roo.Toolbar(tcfg);
41247         if (Roo.isSafari) {
41248             var tbl = tcfg.container.child('table', true);
41249             tbl.setAttribute('width', '100%');
41250         }
41251         */
41252         
41253     }
41254    
41255
41256
41257     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41258 };
41259
41260 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41261     /*
41262      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41263      */
41264     tabPosition : "top",
41265     /*
41266      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41267      */
41268     currentTabWidth : 0,
41269     /*
41270      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41271      */
41272     minTabWidth : 40,
41273     /*
41274      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41275      */
41276     maxTabWidth : 250,
41277     /*
41278      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41279      */
41280     preferredTabWidth : 175,
41281     /*
41282      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41283      */
41284     resizeTabs : false,
41285     /*
41286      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41287      */
41288     monitorResize : true,
41289     /*
41290      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41291      */
41292     toolbar : false,  // set by caller..
41293     
41294     region : false, /// set by caller
41295     
41296     disableTooltips : true, // not used yet...
41297
41298     /**
41299      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41300      * @param {String} id The id of the div to use <b>or create</b>
41301      * @param {String} text The text for the tab
41302      * @param {String} content (optional) Content to put in the TabPanelItem body
41303      * @param {Boolean} closable (optional) True to create a close icon on the tab
41304      * @return {Roo.TabPanelItem} The created TabPanelItem
41305      */
41306     addTab : function(id, text, content, closable, tpl)
41307     {
41308         var item = new Roo.bootstrap.panel.TabItem({
41309             panel: this,
41310             id : id,
41311             text : text,
41312             closable : closable,
41313             tpl : tpl
41314         });
41315         this.addTabItem(item);
41316         if(content){
41317             item.setContent(content);
41318         }
41319         return item;
41320     },
41321
41322     /**
41323      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41324      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41325      * @return {Roo.TabPanelItem}
41326      */
41327     getTab : function(id){
41328         return this.items[id];
41329     },
41330
41331     /**
41332      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41333      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41334      */
41335     hideTab : function(id){
41336         var t = this.items[id];
41337         if(!t.isHidden()){
41338            t.setHidden(true);
41339            this.hiddenCount++;
41340            this.autoSizeTabs();
41341         }
41342     },
41343
41344     /**
41345      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41346      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41347      */
41348     unhideTab : function(id){
41349         var t = this.items[id];
41350         if(t.isHidden()){
41351            t.setHidden(false);
41352            this.hiddenCount--;
41353            this.autoSizeTabs();
41354         }
41355     },
41356
41357     /**
41358      * Adds an existing {@link Roo.TabPanelItem}.
41359      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41360      */
41361     addTabItem : function(item)
41362     {
41363         this.items[item.id] = item;
41364         this.items.push(item);
41365         this.autoSizeTabs();
41366       //  if(this.resizeTabs){
41367     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41368   //         this.autoSizeTabs();
41369 //        }else{
41370 //            item.autoSize();
41371        // }
41372     },
41373
41374     /**
41375      * Removes a {@link Roo.TabPanelItem}.
41376      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41377      */
41378     removeTab : function(id){
41379         var items = this.items;
41380         var tab = items[id];
41381         if(!tab) { return; }
41382         var index = items.indexOf(tab);
41383         if(this.active == tab && items.length > 1){
41384             var newTab = this.getNextAvailable(index);
41385             if(newTab) {
41386                 newTab.activate();
41387             }
41388         }
41389         this.stripEl.dom.removeChild(tab.pnode.dom);
41390         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41391             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41392         }
41393         items.splice(index, 1);
41394         delete this.items[tab.id];
41395         tab.fireEvent("close", tab);
41396         tab.purgeListeners();
41397         this.autoSizeTabs();
41398     },
41399
41400     getNextAvailable : function(start){
41401         var items = this.items;
41402         var index = start;
41403         // look for a next tab that will slide over to
41404         // replace the one being removed
41405         while(index < items.length){
41406             var item = items[++index];
41407             if(item && !item.isHidden()){
41408                 return item;
41409             }
41410         }
41411         // if one isn't found select the previous tab (on the left)
41412         index = start;
41413         while(index >= 0){
41414             var item = items[--index];
41415             if(item && !item.isHidden()){
41416                 return item;
41417             }
41418         }
41419         return null;
41420     },
41421
41422     /**
41423      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41424      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41425      */
41426     disableTab : function(id){
41427         var tab = this.items[id];
41428         if(tab && this.active != tab){
41429             tab.disable();
41430         }
41431     },
41432
41433     /**
41434      * Enables a {@link Roo.TabPanelItem} that is disabled.
41435      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41436      */
41437     enableTab : function(id){
41438         var tab = this.items[id];
41439         tab.enable();
41440     },
41441
41442     /**
41443      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41444      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41445      * @return {Roo.TabPanelItem} The TabPanelItem.
41446      */
41447     activate : function(id)
41448     {
41449         //Roo.log('activite:'  + id);
41450         
41451         var tab = this.items[id];
41452         if(!tab){
41453             return null;
41454         }
41455         if(tab == this.active || tab.disabled){
41456             return tab;
41457         }
41458         var e = {};
41459         this.fireEvent("beforetabchange", this, e, tab);
41460         if(e.cancel !== true && !tab.disabled){
41461             if(this.active){
41462                 this.active.hide();
41463             }
41464             this.active = this.items[id];
41465             this.active.show();
41466             this.fireEvent("tabchange", this, this.active);
41467         }
41468         return tab;
41469     },
41470
41471     /**
41472      * Gets the active {@link Roo.TabPanelItem}.
41473      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41474      */
41475     getActiveTab : function(){
41476         return this.active;
41477     },
41478
41479     /**
41480      * Updates the tab body element to fit the height of the container element
41481      * for overflow scrolling
41482      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41483      */
41484     syncHeight : function(targetHeight){
41485         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41486         var bm = this.bodyEl.getMargins();
41487         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41488         this.bodyEl.setHeight(newHeight);
41489         return newHeight;
41490     },
41491
41492     onResize : function(){
41493         if(this.monitorResize){
41494             this.autoSizeTabs();
41495         }
41496     },
41497
41498     /**
41499      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41500      */
41501     beginUpdate : function(){
41502         this.updating = true;
41503     },
41504
41505     /**
41506      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41507      */
41508     endUpdate : function(){
41509         this.updating = false;
41510         this.autoSizeTabs();
41511     },
41512
41513     /**
41514      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41515      */
41516     autoSizeTabs : function()
41517     {
41518         var count = this.items.length;
41519         var vcount = count - this.hiddenCount;
41520         
41521         if (vcount < 2) {
41522             this.stripEl.hide();
41523         } else {
41524             this.stripEl.show();
41525         }
41526         
41527         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41528             return;
41529         }
41530         
41531         
41532         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41533         var availWidth = Math.floor(w / vcount);
41534         var b = this.stripBody;
41535         if(b.getWidth() > w){
41536             var tabs = this.items;
41537             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41538             if(availWidth < this.minTabWidth){
41539                 /*if(!this.sleft){    // incomplete scrolling code
41540                     this.createScrollButtons();
41541                 }
41542                 this.showScroll();
41543                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41544             }
41545         }else{
41546             if(this.currentTabWidth < this.preferredTabWidth){
41547                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41548             }
41549         }
41550     },
41551
41552     /**
41553      * Returns the number of tabs in this TabPanel.
41554      * @return {Number}
41555      */
41556      getCount : function(){
41557          return this.items.length;
41558      },
41559
41560     /**
41561      * Resizes all the tabs to the passed width
41562      * @param {Number} The new width
41563      */
41564     setTabWidth : function(width){
41565         this.currentTabWidth = width;
41566         for(var i = 0, len = this.items.length; i < len; i++) {
41567                 if(!this.items[i].isHidden()) {
41568                 this.items[i].setWidth(width);
41569             }
41570         }
41571     },
41572
41573     /**
41574      * Destroys this TabPanel
41575      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41576      */
41577     destroy : function(removeEl){
41578         Roo.EventManager.removeResizeListener(this.onResize, this);
41579         for(var i = 0, len = this.items.length; i < len; i++){
41580             this.items[i].purgeListeners();
41581         }
41582         if(removeEl === true){
41583             this.el.update("");
41584             this.el.remove();
41585         }
41586     },
41587     
41588     createStrip : function(container)
41589     {
41590         var strip = document.createElement("nav");
41591         strip.className = Roo.bootstrap.version == 4 ?
41592             "navbar-light bg-light" : 
41593             "navbar navbar-default"; //"x-tabs-wrap";
41594         container.appendChild(strip);
41595         return strip;
41596     },
41597     
41598     createStripList : function(strip)
41599     {
41600         // div wrapper for retard IE
41601         // returns the "tr" element.
41602         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41603         //'<div class="x-tabs-strip-wrap">'+
41604           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41605           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41606         return strip.firstChild; //.firstChild.firstChild.firstChild;
41607     },
41608     createBody : function(container)
41609     {
41610         var body = document.createElement("div");
41611         Roo.id(body, "tab-body");
41612         //Roo.fly(body).addClass("x-tabs-body");
41613         Roo.fly(body).addClass("tab-content");
41614         container.appendChild(body);
41615         return body;
41616     },
41617     createItemBody :function(bodyEl, id){
41618         var body = Roo.getDom(id);
41619         if(!body){
41620             body = document.createElement("div");
41621             body.id = id;
41622         }
41623         //Roo.fly(body).addClass("x-tabs-item-body");
41624         Roo.fly(body).addClass("tab-pane");
41625          bodyEl.insertBefore(body, bodyEl.firstChild);
41626         return body;
41627     },
41628     /** @private */
41629     createStripElements :  function(stripEl, text, closable, tpl)
41630     {
41631         var td = document.createElement("li"); // was td..
41632         td.className = 'nav-item';
41633         
41634         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41635         
41636         
41637         stripEl.appendChild(td);
41638         /*if(closable){
41639             td.className = "x-tabs-closable";
41640             if(!this.closeTpl){
41641                 this.closeTpl = new Roo.Template(
41642                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41643                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41644                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41645                 );
41646             }
41647             var el = this.closeTpl.overwrite(td, {"text": text});
41648             var close = el.getElementsByTagName("div")[0];
41649             var inner = el.getElementsByTagName("em")[0];
41650             return {"el": el, "close": close, "inner": inner};
41651         } else {
41652         */
41653         // not sure what this is..
41654 //            if(!this.tabTpl){
41655                 //this.tabTpl = new Roo.Template(
41656                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41657                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41658                 //);
41659 //                this.tabTpl = new Roo.Template(
41660 //                   '<a href="#">' +
41661 //                   '<span unselectable="on"' +
41662 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41663 //                            ' >{text}</span></a>'
41664 //                );
41665 //                
41666 //            }
41667
41668
41669             var template = tpl || this.tabTpl || false;
41670             
41671             if(!template){
41672                 template =  new Roo.Template(
41673                         Roo.bootstrap.version == 4 ? 
41674                             (
41675                                 '<a class="nav-link" href="#" unselectable="on"' +
41676                                      (this.disableTooltips ? '' : ' title="{text}"') +
41677                                      ' >{text}</a>'
41678                             ) : (
41679                                 '<a class="nav-link" href="#">' +
41680                                 '<span unselectable="on"' +
41681                                          (this.disableTooltips ? '' : ' title="{text}"') +
41682                                     ' >{text}</span></a>'
41683                             )
41684                 );
41685             }
41686             
41687             switch (typeof(template)) {
41688                 case 'object' :
41689                     break;
41690                 case 'string' :
41691                     template = new Roo.Template(template);
41692                     break;
41693                 default :
41694                     break;
41695             }
41696             
41697             var el = template.overwrite(td, {"text": text});
41698             
41699             var inner = el.getElementsByTagName("span")[0];
41700             
41701             return {"el": el, "inner": inner};
41702             
41703     }
41704         
41705     
41706 });
41707
41708 /**
41709  * @class Roo.TabPanelItem
41710  * @extends Roo.util.Observable
41711  * Represents an individual item (tab plus body) in a TabPanel.
41712  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41713  * @param {String} id The id of this TabPanelItem
41714  * @param {String} text The text for the tab of this TabPanelItem
41715  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41716  */
41717 Roo.bootstrap.panel.TabItem = function(config){
41718     /**
41719      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41720      * @type Roo.TabPanel
41721      */
41722     this.tabPanel = config.panel;
41723     /**
41724      * The id for this TabPanelItem
41725      * @type String
41726      */
41727     this.id = config.id;
41728     /** @private */
41729     this.disabled = false;
41730     /** @private */
41731     this.text = config.text;
41732     /** @private */
41733     this.loaded = false;
41734     this.closable = config.closable;
41735
41736     /**
41737      * The body element for this TabPanelItem.
41738      * @type Roo.Element
41739      */
41740     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41741     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41742     this.bodyEl.setStyle("display", "block");
41743     this.bodyEl.setStyle("zoom", "1");
41744     //this.hideAction();
41745
41746     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41747     /** @private */
41748     this.el = Roo.get(els.el);
41749     this.inner = Roo.get(els.inner, true);
41750      this.textEl = Roo.bootstrap.version == 4 ?
41751         this.el : Roo.get(this.el.dom.firstChild, true);
41752
41753     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41754     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41755
41756     
41757 //    this.el.on("mousedown", this.onTabMouseDown, this);
41758     this.el.on("click", this.onTabClick, this);
41759     /** @private */
41760     if(config.closable){
41761         var c = Roo.get(els.close, true);
41762         c.dom.title = this.closeText;
41763         c.addClassOnOver("close-over");
41764         c.on("click", this.closeClick, this);
41765      }
41766
41767     this.addEvents({
41768          /**
41769          * @event activate
41770          * Fires when this tab becomes the active tab.
41771          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41772          * @param {Roo.TabPanelItem} this
41773          */
41774         "activate": true,
41775         /**
41776          * @event beforeclose
41777          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41778          * @param {Roo.TabPanelItem} this
41779          * @param {Object} e Set cancel to true on this object to cancel the close.
41780          */
41781         "beforeclose": true,
41782         /**
41783          * @event close
41784          * Fires when this tab is closed.
41785          * @param {Roo.TabPanelItem} this
41786          */
41787          "close": true,
41788         /**
41789          * @event deactivate
41790          * Fires when this tab is no longer the active tab.
41791          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41792          * @param {Roo.TabPanelItem} this
41793          */
41794          "deactivate" : true
41795     });
41796     this.hidden = false;
41797
41798     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41799 };
41800
41801 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41802            {
41803     purgeListeners : function(){
41804        Roo.util.Observable.prototype.purgeListeners.call(this);
41805        this.el.removeAllListeners();
41806     },
41807     /**
41808      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41809      */
41810     show : function(){
41811         this.status_node.addClass("active");
41812         this.showAction();
41813         if(Roo.isOpera){
41814             this.tabPanel.stripWrap.repaint();
41815         }
41816         this.fireEvent("activate", this.tabPanel, this);
41817     },
41818
41819     /**
41820      * Returns true if this tab is the active tab.
41821      * @return {Boolean}
41822      */
41823     isActive : function(){
41824         return this.tabPanel.getActiveTab() == this;
41825     },
41826
41827     /**
41828      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41829      */
41830     hide : function(){
41831         this.status_node.removeClass("active");
41832         this.hideAction();
41833         this.fireEvent("deactivate", this.tabPanel, this);
41834     },
41835
41836     hideAction : function(){
41837         this.bodyEl.hide();
41838         this.bodyEl.setStyle("position", "absolute");
41839         this.bodyEl.setLeft("-20000px");
41840         this.bodyEl.setTop("-20000px");
41841     },
41842
41843     showAction : function(){
41844         this.bodyEl.setStyle("position", "relative");
41845         this.bodyEl.setTop("");
41846         this.bodyEl.setLeft("");
41847         this.bodyEl.show();
41848     },
41849
41850     /**
41851      * Set the tooltip for the tab.
41852      * @param {String} tooltip The tab's tooltip
41853      */
41854     setTooltip : function(text){
41855         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41856             this.textEl.dom.qtip = text;
41857             this.textEl.dom.removeAttribute('title');
41858         }else{
41859             this.textEl.dom.title = text;
41860         }
41861     },
41862
41863     onTabClick : function(e){
41864         e.preventDefault();
41865         this.tabPanel.activate(this.id);
41866     },
41867
41868     onTabMouseDown : function(e){
41869         e.preventDefault();
41870         this.tabPanel.activate(this.id);
41871     },
41872 /*
41873     getWidth : function(){
41874         return this.inner.getWidth();
41875     },
41876
41877     setWidth : function(width){
41878         var iwidth = width - this.linode.getPadding("lr");
41879         this.inner.setWidth(iwidth);
41880         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41881         this.linode.setWidth(width);
41882     },
41883 */
41884     /**
41885      * Show or hide the tab
41886      * @param {Boolean} hidden True to hide or false to show.
41887      */
41888     setHidden : function(hidden){
41889         this.hidden = hidden;
41890         this.linode.setStyle("display", hidden ? "none" : "");
41891     },
41892
41893     /**
41894      * Returns true if this tab is "hidden"
41895      * @return {Boolean}
41896      */
41897     isHidden : function(){
41898         return this.hidden;
41899     },
41900
41901     /**
41902      * Returns the text for this tab
41903      * @return {String}
41904      */
41905     getText : function(){
41906         return this.text;
41907     },
41908     /*
41909     autoSize : function(){
41910         //this.el.beginMeasure();
41911         this.textEl.setWidth(1);
41912         /*
41913          *  #2804 [new] Tabs in Roojs
41914          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41915          */
41916         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41917         //this.el.endMeasure();
41918     //},
41919
41920     /**
41921      * Sets the text for the tab (Note: this also sets the tooltip text)
41922      * @param {String} text The tab's text and tooltip
41923      */
41924     setText : function(text){
41925         this.text = text;
41926         this.textEl.update(text);
41927         this.setTooltip(text);
41928         //if(!this.tabPanel.resizeTabs){
41929         //    this.autoSize();
41930         //}
41931     },
41932     /**
41933      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41934      */
41935     activate : function(){
41936         this.tabPanel.activate(this.id);
41937     },
41938
41939     /**
41940      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41941      */
41942     disable : function(){
41943         if(this.tabPanel.active != this){
41944             this.disabled = true;
41945             this.status_node.addClass("disabled");
41946         }
41947     },
41948
41949     /**
41950      * Enables this TabPanelItem if it was previously disabled.
41951      */
41952     enable : function(){
41953         this.disabled = false;
41954         this.status_node.removeClass("disabled");
41955     },
41956
41957     /**
41958      * Sets the content for this TabPanelItem.
41959      * @param {String} content The content
41960      * @param {Boolean} loadScripts true to look for and load scripts
41961      */
41962     setContent : function(content, loadScripts){
41963         this.bodyEl.update(content, loadScripts);
41964     },
41965
41966     /**
41967      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41968      * @return {Roo.UpdateManager} The UpdateManager
41969      */
41970     getUpdateManager : function(){
41971         return this.bodyEl.getUpdateManager();
41972     },
41973
41974     /**
41975      * Set a URL to be used to load the content for this TabPanelItem.
41976      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41977      * @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)
41978      * @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)
41979      * @return {Roo.UpdateManager} The UpdateManager
41980      */
41981     setUrl : function(url, params, loadOnce){
41982         if(this.refreshDelegate){
41983             this.un('activate', this.refreshDelegate);
41984         }
41985         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41986         this.on("activate", this.refreshDelegate);
41987         return this.bodyEl.getUpdateManager();
41988     },
41989
41990     /** @private */
41991     _handleRefresh : function(url, params, loadOnce){
41992         if(!loadOnce || !this.loaded){
41993             var updater = this.bodyEl.getUpdateManager();
41994             updater.update(url, params, this._setLoaded.createDelegate(this));
41995         }
41996     },
41997
41998     /**
41999      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42000      *   Will fail silently if the setUrl method has not been called.
42001      *   This does not activate the panel, just updates its content.
42002      */
42003     refresh : function(){
42004         if(this.refreshDelegate){
42005            this.loaded = false;
42006            this.refreshDelegate();
42007         }
42008     },
42009
42010     /** @private */
42011     _setLoaded : function(){
42012         this.loaded = true;
42013     },
42014
42015     /** @private */
42016     closeClick : function(e){
42017         var o = {};
42018         e.stopEvent();
42019         this.fireEvent("beforeclose", this, o);
42020         if(o.cancel !== true){
42021             this.tabPanel.removeTab(this.id);
42022         }
42023     },
42024     /**
42025      * The text displayed in the tooltip for the close icon.
42026      * @type String
42027      */
42028     closeText : "Close this tab"
42029 });
42030 /**
42031 *    This script refer to:
42032 *    Title: International Telephone Input
42033 *    Author: Jack O'Connor
42034 *    Code version:  v12.1.12
42035 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42036 **/
42037
42038 Roo.bootstrap.PhoneInputData = function() {
42039     var d = [
42040       [
42041         "Afghanistan (‫افغانستان‬‎)",
42042         "af",
42043         "93"
42044       ],
42045       [
42046         "Albania (Shqipëri)",
42047         "al",
42048         "355"
42049       ],
42050       [
42051         "Algeria (‫الجزائر‬‎)",
42052         "dz",
42053         "213"
42054       ],
42055       [
42056         "American Samoa",
42057         "as",
42058         "1684"
42059       ],
42060       [
42061         "Andorra",
42062         "ad",
42063         "376"
42064       ],
42065       [
42066         "Angola",
42067         "ao",
42068         "244"
42069       ],
42070       [
42071         "Anguilla",
42072         "ai",
42073         "1264"
42074       ],
42075       [
42076         "Antigua and Barbuda",
42077         "ag",
42078         "1268"
42079       ],
42080       [
42081         "Argentina",
42082         "ar",
42083         "54"
42084       ],
42085       [
42086         "Armenia (Հայաստան)",
42087         "am",
42088         "374"
42089       ],
42090       [
42091         "Aruba",
42092         "aw",
42093         "297"
42094       ],
42095       [
42096         "Australia",
42097         "au",
42098         "61",
42099         0
42100       ],
42101       [
42102         "Austria (Österreich)",
42103         "at",
42104         "43"
42105       ],
42106       [
42107         "Azerbaijan (Azərbaycan)",
42108         "az",
42109         "994"
42110       ],
42111       [
42112         "Bahamas",
42113         "bs",
42114         "1242"
42115       ],
42116       [
42117         "Bahrain (‫البحرين‬‎)",
42118         "bh",
42119         "973"
42120       ],
42121       [
42122         "Bangladesh (বাংলাদেশ)",
42123         "bd",
42124         "880"
42125       ],
42126       [
42127         "Barbados",
42128         "bb",
42129         "1246"
42130       ],
42131       [
42132         "Belarus (Беларусь)",
42133         "by",
42134         "375"
42135       ],
42136       [
42137         "Belgium (België)",
42138         "be",
42139         "32"
42140       ],
42141       [
42142         "Belize",
42143         "bz",
42144         "501"
42145       ],
42146       [
42147         "Benin (Bénin)",
42148         "bj",
42149         "229"
42150       ],
42151       [
42152         "Bermuda",
42153         "bm",
42154         "1441"
42155       ],
42156       [
42157         "Bhutan (འབྲུག)",
42158         "bt",
42159         "975"
42160       ],
42161       [
42162         "Bolivia",
42163         "bo",
42164         "591"
42165       ],
42166       [
42167         "Bosnia and Herzegovina (Босна и Херцеговина)",
42168         "ba",
42169         "387"
42170       ],
42171       [
42172         "Botswana",
42173         "bw",
42174         "267"
42175       ],
42176       [
42177         "Brazil (Brasil)",
42178         "br",
42179         "55"
42180       ],
42181       [
42182         "British Indian Ocean Territory",
42183         "io",
42184         "246"
42185       ],
42186       [
42187         "British Virgin Islands",
42188         "vg",
42189         "1284"
42190       ],
42191       [
42192         "Brunei",
42193         "bn",
42194         "673"
42195       ],
42196       [
42197         "Bulgaria (България)",
42198         "bg",
42199         "359"
42200       ],
42201       [
42202         "Burkina Faso",
42203         "bf",
42204         "226"
42205       ],
42206       [
42207         "Burundi (Uburundi)",
42208         "bi",
42209         "257"
42210       ],
42211       [
42212         "Cambodia (កម្ពុជា)",
42213         "kh",
42214         "855"
42215       ],
42216       [
42217         "Cameroon (Cameroun)",
42218         "cm",
42219         "237"
42220       ],
42221       [
42222         "Canada",
42223         "ca",
42224         "1",
42225         1,
42226         ["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"]
42227       ],
42228       [
42229         "Cape Verde (Kabu Verdi)",
42230         "cv",
42231         "238"
42232       ],
42233       [
42234         "Caribbean Netherlands",
42235         "bq",
42236         "599",
42237         1
42238       ],
42239       [
42240         "Cayman Islands",
42241         "ky",
42242         "1345"
42243       ],
42244       [
42245         "Central African Republic (République centrafricaine)",
42246         "cf",
42247         "236"
42248       ],
42249       [
42250         "Chad (Tchad)",
42251         "td",
42252         "235"
42253       ],
42254       [
42255         "Chile",
42256         "cl",
42257         "56"
42258       ],
42259       [
42260         "China (中国)",
42261         "cn",
42262         "86"
42263       ],
42264       [
42265         "Christmas Island",
42266         "cx",
42267         "61",
42268         2
42269       ],
42270       [
42271         "Cocos (Keeling) Islands",
42272         "cc",
42273         "61",
42274         1
42275       ],
42276       [
42277         "Colombia",
42278         "co",
42279         "57"
42280       ],
42281       [
42282         "Comoros (‫جزر القمر‬‎)",
42283         "km",
42284         "269"
42285       ],
42286       [
42287         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42288         "cd",
42289         "243"
42290       ],
42291       [
42292         "Congo (Republic) (Congo-Brazzaville)",
42293         "cg",
42294         "242"
42295       ],
42296       [
42297         "Cook Islands",
42298         "ck",
42299         "682"
42300       ],
42301       [
42302         "Costa Rica",
42303         "cr",
42304         "506"
42305       ],
42306       [
42307         "Côte d’Ivoire",
42308         "ci",
42309         "225"
42310       ],
42311       [
42312         "Croatia (Hrvatska)",
42313         "hr",
42314         "385"
42315       ],
42316       [
42317         "Cuba",
42318         "cu",
42319         "53"
42320       ],
42321       [
42322         "Curaçao",
42323         "cw",
42324         "599",
42325         0
42326       ],
42327       [
42328         "Cyprus (Κύπρος)",
42329         "cy",
42330         "357"
42331       ],
42332       [
42333         "Czech Republic (Česká republika)",
42334         "cz",
42335         "420"
42336       ],
42337       [
42338         "Denmark (Danmark)",
42339         "dk",
42340         "45"
42341       ],
42342       [
42343         "Djibouti",
42344         "dj",
42345         "253"
42346       ],
42347       [
42348         "Dominica",
42349         "dm",
42350         "1767"
42351       ],
42352       [
42353         "Dominican Republic (República Dominicana)",
42354         "do",
42355         "1",
42356         2,
42357         ["809", "829", "849"]
42358       ],
42359       [
42360         "Ecuador",
42361         "ec",
42362         "593"
42363       ],
42364       [
42365         "Egypt (‫مصر‬‎)",
42366         "eg",
42367         "20"
42368       ],
42369       [
42370         "El Salvador",
42371         "sv",
42372         "503"
42373       ],
42374       [
42375         "Equatorial Guinea (Guinea Ecuatorial)",
42376         "gq",
42377         "240"
42378       ],
42379       [
42380         "Eritrea",
42381         "er",
42382         "291"
42383       ],
42384       [
42385         "Estonia (Eesti)",
42386         "ee",
42387         "372"
42388       ],
42389       [
42390         "Ethiopia",
42391         "et",
42392         "251"
42393       ],
42394       [
42395         "Falkland Islands (Islas Malvinas)",
42396         "fk",
42397         "500"
42398       ],
42399       [
42400         "Faroe Islands (Føroyar)",
42401         "fo",
42402         "298"
42403       ],
42404       [
42405         "Fiji",
42406         "fj",
42407         "679"
42408       ],
42409       [
42410         "Finland (Suomi)",
42411         "fi",
42412         "358",
42413         0
42414       ],
42415       [
42416         "France",
42417         "fr",
42418         "33"
42419       ],
42420       [
42421         "French Guiana (Guyane française)",
42422         "gf",
42423         "594"
42424       ],
42425       [
42426         "French Polynesia (Polynésie française)",
42427         "pf",
42428         "689"
42429       ],
42430       [
42431         "Gabon",
42432         "ga",
42433         "241"
42434       ],
42435       [
42436         "Gambia",
42437         "gm",
42438         "220"
42439       ],
42440       [
42441         "Georgia (საქართველო)",
42442         "ge",
42443         "995"
42444       ],
42445       [
42446         "Germany (Deutschland)",
42447         "de",
42448         "49"
42449       ],
42450       [
42451         "Ghana (Gaana)",
42452         "gh",
42453         "233"
42454       ],
42455       [
42456         "Gibraltar",
42457         "gi",
42458         "350"
42459       ],
42460       [
42461         "Greece (Ελλάδα)",
42462         "gr",
42463         "30"
42464       ],
42465       [
42466         "Greenland (Kalaallit Nunaat)",
42467         "gl",
42468         "299"
42469       ],
42470       [
42471         "Grenada",
42472         "gd",
42473         "1473"
42474       ],
42475       [
42476         "Guadeloupe",
42477         "gp",
42478         "590",
42479         0
42480       ],
42481       [
42482         "Guam",
42483         "gu",
42484         "1671"
42485       ],
42486       [
42487         "Guatemala",
42488         "gt",
42489         "502"
42490       ],
42491       [
42492         "Guernsey",
42493         "gg",
42494         "44",
42495         1
42496       ],
42497       [
42498         "Guinea (Guinée)",
42499         "gn",
42500         "224"
42501       ],
42502       [
42503         "Guinea-Bissau (Guiné Bissau)",
42504         "gw",
42505         "245"
42506       ],
42507       [
42508         "Guyana",
42509         "gy",
42510         "592"
42511       ],
42512       [
42513         "Haiti",
42514         "ht",
42515         "509"
42516       ],
42517       [
42518         "Honduras",
42519         "hn",
42520         "504"
42521       ],
42522       [
42523         "Hong Kong (香港)",
42524         "hk",
42525         "852"
42526       ],
42527       [
42528         "Hungary (Magyarország)",
42529         "hu",
42530         "36"
42531       ],
42532       [
42533         "Iceland (Ísland)",
42534         "is",
42535         "354"
42536       ],
42537       [
42538         "India (भारत)",
42539         "in",
42540         "91"
42541       ],
42542       [
42543         "Indonesia",
42544         "id",
42545         "62"
42546       ],
42547       [
42548         "Iran (‫ایران‬‎)",
42549         "ir",
42550         "98"
42551       ],
42552       [
42553         "Iraq (‫العراق‬‎)",
42554         "iq",
42555         "964"
42556       ],
42557       [
42558         "Ireland",
42559         "ie",
42560         "353"
42561       ],
42562       [
42563         "Isle of Man",
42564         "im",
42565         "44",
42566         2
42567       ],
42568       [
42569         "Israel (‫ישראל‬‎)",
42570         "il",
42571         "972"
42572       ],
42573       [
42574         "Italy (Italia)",
42575         "it",
42576         "39",
42577         0
42578       ],
42579       [
42580         "Jamaica",
42581         "jm",
42582         "1876"
42583       ],
42584       [
42585         "Japan (日本)",
42586         "jp",
42587         "81"
42588       ],
42589       [
42590         "Jersey",
42591         "je",
42592         "44",
42593         3
42594       ],
42595       [
42596         "Jordan (‫الأردن‬‎)",
42597         "jo",
42598         "962"
42599       ],
42600       [
42601         "Kazakhstan (Казахстан)",
42602         "kz",
42603         "7",
42604         1
42605       ],
42606       [
42607         "Kenya",
42608         "ke",
42609         "254"
42610       ],
42611       [
42612         "Kiribati",
42613         "ki",
42614         "686"
42615       ],
42616       [
42617         "Kosovo",
42618         "xk",
42619         "383"
42620       ],
42621       [
42622         "Kuwait (‫الكويت‬‎)",
42623         "kw",
42624         "965"
42625       ],
42626       [
42627         "Kyrgyzstan (Кыргызстан)",
42628         "kg",
42629         "996"
42630       ],
42631       [
42632         "Laos (ລາວ)",
42633         "la",
42634         "856"
42635       ],
42636       [
42637         "Latvia (Latvija)",
42638         "lv",
42639         "371"
42640       ],
42641       [
42642         "Lebanon (‫لبنان‬‎)",
42643         "lb",
42644         "961"
42645       ],
42646       [
42647         "Lesotho",
42648         "ls",
42649         "266"
42650       ],
42651       [
42652         "Liberia",
42653         "lr",
42654         "231"
42655       ],
42656       [
42657         "Libya (‫ليبيا‬‎)",
42658         "ly",
42659         "218"
42660       ],
42661       [
42662         "Liechtenstein",
42663         "li",
42664         "423"
42665       ],
42666       [
42667         "Lithuania (Lietuva)",
42668         "lt",
42669         "370"
42670       ],
42671       [
42672         "Luxembourg",
42673         "lu",
42674         "352"
42675       ],
42676       [
42677         "Macau (澳門)",
42678         "mo",
42679         "853"
42680       ],
42681       [
42682         "Macedonia (FYROM) (Македонија)",
42683         "mk",
42684         "389"
42685       ],
42686       [
42687         "Madagascar (Madagasikara)",
42688         "mg",
42689         "261"
42690       ],
42691       [
42692         "Malawi",
42693         "mw",
42694         "265"
42695       ],
42696       [
42697         "Malaysia",
42698         "my",
42699         "60"
42700       ],
42701       [
42702         "Maldives",
42703         "mv",
42704         "960"
42705       ],
42706       [
42707         "Mali",
42708         "ml",
42709         "223"
42710       ],
42711       [
42712         "Malta",
42713         "mt",
42714         "356"
42715       ],
42716       [
42717         "Marshall Islands",
42718         "mh",
42719         "692"
42720       ],
42721       [
42722         "Martinique",
42723         "mq",
42724         "596"
42725       ],
42726       [
42727         "Mauritania (‫موريتانيا‬‎)",
42728         "mr",
42729         "222"
42730       ],
42731       [
42732         "Mauritius (Moris)",
42733         "mu",
42734         "230"
42735       ],
42736       [
42737         "Mayotte",
42738         "yt",
42739         "262",
42740         1
42741       ],
42742       [
42743         "Mexico (México)",
42744         "mx",
42745         "52"
42746       ],
42747       [
42748         "Micronesia",
42749         "fm",
42750         "691"
42751       ],
42752       [
42753         "Moldova (Republica Moldova)",
42754         "md",
42755         "373"
42756       ],
42757       [
42758         "Monaco",
42759         "mc",
42760         "377"
42761       ],
42762       [
42763         "Mongolia (Монгол)",
42764         "mn",
42765         "976"
42766       ],
42767       [
42768         "Montenegro (Crna Gora)",
42769         "me",
42770         "382"
42771       ],
42772       [
42773         "Montserrat",
42774         "ms",
42775         "1664"
42776       ],
42777       [
42778         "Morocco (‫المغرب‬‎)",
42779         "ma",
42780         "212",
42781         0
42782       ],
42783       [
42784         "Mozambique (Moçambique)",
42785         "mz",
42786         "258"
42787       ],
42788       [
42789         "Myanmar (Burma) (မြန်မာ)",
42790         "mm",
42791         "95"
42792       ],
42793       [
42794         "Namibia (Namibië)",
42795         "na",
42796         "264"
42797       ],
42798       [
42799         "Nauru",
42800         "nr",
42801         "674"
42802       ],
42803       [
42804         "Nepal (नेपाल)",
42805         "np",
42806         "977"
42807       ],
42808       [
42809         "Netherlands (Nederland)",
42810         "nl",
42811         "31"
42812       ],
42813       [
42814         "New Caledonia (Nouvelle-Calédonie)",
42815         "nc",
42816         "687"
42817       ],
42818       [
42819         "New Zealand",
42820         "nz",
42821         "64"
42822       ],
42823       [
42824         "Nicaragua",
42825         "ni",
42826         "505"
42827       ],
42828       [
42829         "Niger (Nijar)",
42830         "ne",
42831         "227"
42832       ],
42833       [
42834         "Nigeria",
42835         "ng",
42836         "234"
42837       ],
42838       [
42839         "Niue",
42840         "nu",
42841         "683"
42842       ],
42843       [
42844         "Norfolk Island",
42845         "nf",
42846         "672"
42847       ],
42848       [
42849         "North Korea (조선 민주주의 인민 공화국)",
42850         "kp",
42851         "850"
42852       ],
42853       [
42854         "Northern Mariana Islands",
42855         "mp",
42856         "1670"
42857       ],
42858       [
42859         "Norway (Norge)",
42860         "no",
42861         "47",
42862         0
42863       ],
42864       [
42865         "Oman (‫عُمان‬‎)",
42866         "om",
42867         "968"
42868       ],
42869       [
42870         "Pakistan (‫پاکستان‬‎)",
42871         "pk",
42872         "92"
42873       ],
42874       [
42875         "Palau",
42876         "pw",
42877         "680"
42878       ],
42879       [
42880         "Palestine (‫فلسطين‬‎)",
42881         "ps",
42882         "970"
42883       ],
42884       [
42885         "Panama (Panamá)",
42886         "pa",
42887         "507"
42888       ],
42889       [
42890         "Papua New Guinea",
42891         "pg",
42892         "675"
42893       ],
42894       [
42895         "Paraguay",
42896         "py",
42897         "595"
42898       ],
42899       [
42900         "Peru (Perú)",
42901         "pe",
42902         "51"
42903       ],
42904       [
42905         "Philippines",
42906         "ph",
42907         "63"
42908       ],
42909       [
42910         "Poland (Polska)",
42911         "pl",
42912         "48"
42913       ],
42914       [
42915         "Portugal",
42916         "pt",
42917         "351"
42918       ],
42919       [
42920         "Puerto Rico",
42921         "pr",
42922         "1",
42923         3,
42924         ["787", "939"]
42925       ],
42926       [
42927         "Qatar (‫قطر‬‎)",
42928         "qa",
42929         "974"
42930       ],
42931       [
42932         "Réunion (La Réunion)",
42933         "re",
42934         "262",
42935         0
42936       ],
42937       [
42938         "Romania (România)",
42939         "ro",
42940         "40"
42941       ],
42942       [
42943         "Russia (Россия)",
42944         "ru",
42945         "7",
42946         0
42947       ],
42948       [
42949         "Rwanda",
42950         "rw",
42951         "250"
42952       ],
42953       [
42954         "Saint Barthélemy",
42955         "bl",
42956         "590",
42957         1
42958       ],
42959       [
42960         "Saint Helena",
42961         "sh",
42962         "290"
42963       ],
42964       [
42965         "Saint Kitts and Nevis",
42966         "kn",
42967         "1869"
42968       ],
42969       [
42970         "Saint Lucia",
42971         "lc",
42972         "1758"
42973       ],
42974       [
42975         "Saint Martin (Saint-Martin (partie française))",
42976         "mf",
42977         "590",
42978         2
42979       ],
42980       [
42981         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42982         "pm",
42983         "508"
42984       ],
42985       [
42986         "Saint Vincent and the Grenadines",
42987         "vc",
42988         "1784"
42989       ],
42990       [
42991         "Samoa",
42992         "ws",
42993         "685"
42994       ],
42995       [
42996         "San Marino",
42997         "sm",
42998         "378"
42999       ],
43000       [
43001         "São Tomé and Príncipe (São Tomé e Príncipe)",
43002         "st",
43003         "239"
43004       ],
43005       [
43006         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43007         "sa",
43008         "966"
43009       ],
43010       [
43011         "Senegal (Sénégal)",
43012         "sn",
43013         "221"
43014       ],
43015       [
43016         "Serbia (Србија)",
43017         "rs",
43018         "381"
43019       ],
43020       [
43021         "Seychelles",
43022         "sc",
43023         "248"
43024       ],
43025       [
43026         "Sierra Leone",
43027         "sl",
43028         "232"
43029       ],
43030       [
43031         "Singapore",
43032         "sg",
43033         "65"
43034       ],
43035       [
43036         "Sint Maarten",
43037         "sx",
43038         "1721"
43039       ],
43040       [
43041         "Slovakia (Slovensko)",
43042         "sk",
43043         "421"
43044       ],
43045       [
43046         "Slovenia (Slovenija)",
43047         "si",
43048         "386"
43049       ],
43050       [
43051         "Solomon Islands",
43052         "sb",
43053         "677"
43054       ],
43055       [
43056         "Somalia (Soomaaliya)",
43057         "so",
43058         "252"
43059       ],
43060       [
43061         "South Africa",
43062         "za",
43063         "27"
43064       ],
43065       [
43066         "South Korea (대한민국)",
43067         "kr",
43068         "82"
43069       ],
43070       [
43071         "South Sudan (‫جنوب السودان‬‎)",
43072         "ss",
43073         "211"
43074       ],
43075       [
43076         "Spain (España)",
43077         "es",
43078         "34"
43079       ],
43080       [
43081         "Sri Lanka (ශ්‍රී ලංකාව)",
43082         "lk",
43083         "94"
43084       ],
43085       [
43086         "Sudan (‫السودان‬‎)",
43087         "sd",
43088         "249"
43089       ],
43090       [
43091         "Suriname",
43092         "sr",
43093         "597"
43094       ],
43095       [
43096         "Svalbard and Jan Mayen",
43097         "sj",
43098         "47",
43099         1
43100       ],
43101       [
43102         "Swaziland",
43103         "sz",
43104         "268"
43105       ],
43106       [
43107         "Sweden (Sverige)",
43108         "se",
43109         "46"
43110       ],
43111       [
43112         "Switzerland (Schweiz)",
43113         "ch",
43114         "41"
43115       ],
43116       [
43117         "Syria (‫سوريا‬‎)",
43118         "sy",
43119         "963"
43120       ],
43121       [
43122         "Taiwan (台灣)",
43123         "tw",
43124         "886"
43125       ],
43126       [
43127         "Tajikistan",
43128         "tj",
43129         "992"
43130       ],
43131       [
43132         "Tanzania",
43133         "tz",
43134         "255"
43135       ],
43136       [
43137         "Thailand (ไทย)",
43138         "th",
43139         "66"
43140       ],
43141       [
43142         "Timor-Leste",
43143         "tl",
43144         "670"
43145       ],
43146       [
43147         "Togo",
43148         "tg",
43149         "228"
43150       ],
43151       [
43152         "Tokelau",
43153         "tk",
43154         "690"
43155       ],
43156       [
43157         "Tonga",
43158         "to",
43159         "676"
43160       ],
43161       [
43162         "Trinidad and Tobago",
43163         "tt",
43164         "1868"
43165       ],
43166       [
43167         "Tunisia (‫تونس‬‎)",
43168         "tn",
43169         "216"
43170       ],
43171       [
43172         "Turkey (Türkiye)",
43173         "tr",
43174         "90"
43175       ],
43176       [
43177         "Turkmenistan",
43178         "tm",
43179         "993"
43180       ],
43181       [
43182         "Turks and Caicos Islands",
43183         "tc",
43184         "1649"
43185       ],
43186       [
43187         "Tuvalu",
43188         "tv",
43189         "688"
43190       ],
43191       [
43192         "U.S. Virgin Islands",
43193         "vi",
43194         "1340"
43195       ],
43196       [
43197         "Uganda",
43198         "ug",
43199         "256"
43200       ],
43201       [
43202         "Ukraine (Україна)",
43203         "ua",
43204         "380"
43205       ],
43206       [
43207         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43208         "ae",
43209         "971"
43210       ],
43211       [
43212         "United Kingdom",
43213         "gb",
43214         "44",
43215         0
43216       ],
43217       [
43218         "United States",
43219         "us",
43220         "1",
43221         0
43222       ],
43223       [
43224         "Uruguay",
43225         "uy",
43226         "598"
43227       ],
43228       [
43229         "Uzbekistan (Oʻzbekiston)",
43230         "uz",
43231         "998"
43232       ],
43233       [
43234         "Vanuatu",
43235         "vu",
43236         "678"
43237       ],
43238       [
43239         "Vatican City (Città del Vaticano)",
43240         "va",
43241         "39",
43242         1
43243       ],
43244       [
43245         "Venezuela",
43246         "ve",
43247         "58"
43248       ],
43249       [
43250         "Vietnam (Việt Nam)",
43251         "vn",
43252         "84"
43253       ],
43254       [
43255         "Wallis and Futuna (Wallis-et-Futuna)",
43256         "wf",
43257         "681"
43258       ],
43259       [
43260         "Western Sahara (‫الصحراء الغربية‬‎)",
43261         "eh",
43262         "212",
43263         1
43264       ],
43265       [
43266         "Yemen (‫اليمن‬‎)",
43267         "ye",
43268         "967"
43269       ],
43270       [
43271         "Zambia",
43272         "zm",
43273         "260"
43274       ],
43275       [
43276         "Zimbabwe",
43277         "zw",
43278         "263"
43279       ],
43280       [
43281         "Åland Islands",
43282         "ax",
43283         "358",
43284         1
43285       ]
43286   ];
43287   
43288   return d;
43289 }/**
43290 *    This script refer to:
43291 *    Title: International Telephone Input
43292 *    Author: Jack O'Connor
43293 *    Code version:  v12.1.12
43294 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43295 **/
43296
43297 /**
43298  * @class Roo.bootstrap.PhoneInput
43299  * @extends Roo.bootstrap.TriggerField
43300  * An input with International dial-code selection
43301  
43302  * @cfg {String} defaultDialCode default '+852'
43303  * @cfg {Array} preferedCountries default []
43304   
43305  * @constructor
43306  * Create a new PhoneInput.
43307  * @param {Object} config Configuration options
43308  */
43309
43310 Roo.bootstrap.PhoneInput = function(config) {
43311     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43312 };
43313
43314 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43315         /**
43316         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43317         */
43318         listWidth: undefined,
43319         
43320         selectedClass: 'active',
43321         
43322         invalidClass : "has-warning",
43323         
43324         validClass: 'has-success',
43325         
43326         allowed: '0123456789',
43327         
43328         max_length: 15,
43329         
43330         /**
43331          * @cfg {String} defaultDialCode The default dial code when initializing the input
43332          */
43333         defaultDialCode: '+852',
43334         
43335         /**
43336          * @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
43337          */
43338         preferedCountries: false,
43339         
43340         getAutoCreate : function()
43341         {
43342             var data = Roo.bootstrap.PhoneInputData();
43343             var align = this.labelAlign || this.parentLabelAlign();
43344             var id = Roo.id();
43345             
43346             this.allCountries = [];
43347             this.dialCodeMapping = [];
43348             
43349             for (var i = 0; i < data.length; i++) {
43350               var c = data[i];
43351               this.allCountries[i] = {
43352                 name: c[0],
43353                 iso2: c[1],
43354                 dialCode: c[2],
43355                 priority: c[3] || 0,
43356                 areaCodes: c[4] || null
43357               };
43358               this.dialCodeMapping[c[2]] = {
43359                   name: c[0],
43360                   iso2: c[1],
43361                   priority: c[3] || 0,
43362                   areaCodes: c[4] || null
43363               };
43364             }
43365             
43366             var cfg = {
43367                 cls: 'form-group',
43368                 cn: []
43369             };
43370             
43371             var input =  {
43372                 tag: 'input',
43373                 id : id,
43374                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43375                 maxlength: this.max_length,
43376                 cls : 'form-control tel-input',
43377                 autocomplete: 'new-password'
43378             };
43379             
43380             var hiddenInput = {
43381                 tag: 'input',
43382                 type: 'hidden',
43383                 cls: 'hidden-tel-input'
43384             };
43385             
43386             if (this.name) {
43387                 hiddenInput.name = this.name;
43388             }
43389             
43390             if (this.disabled) {
43391                 input.disabled = true;
43392             }
43393             
43394             var flag_container = {
43395                 tag: 'div',
43396                 cls: 'flag-box',
43397                 cn: [
43398                     {
43399                         tag: 'div',
43400                         cls: 'flag'
43401                     },
43402                     {
43403                         tag: 'div',
43404                         cls: 'caret'
43405                     }
43406                 ]
43407             };
43408             
43409             var box = {
43410                 tag: 'div',
43411                 cls: this.hasFeedback ? 'has-feedback' : '',
43412                 cn: [
43413                     hiddenInput,
43414                     input,
43415                     {
43416                         tag: 'input',
43417                         cls: 'dial-code-holder',
43418                         disabled: true
43419                     }
43420                 ]
43421             };
43422             
43423             var container = {
43424                 cls: 'roo-select2-container input-group',
43425                 cn: [
43426                     flag_container,
43427                     box
43428                 ]
43429             };
43430             
43431             if (this.fieldLabel.length) {
43432                 var indicator = {
43433                     tag: 'i',
43434                     tooltip: 'This field is required'
43435                 };
43436                 
43437                 var label = {
43438                     tag: 'label',
43439                     'for':  id,
43440                     cls: 'control-label',
43441                     cn: []
43442                 };
43443                 
43444                 var label_text = {
43445                     tag: 'span',
43446                     html: this.fieldLabel
43447                 };
43448                 
43449                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43450                 label.cn = [
43451                     indicator,
43452                     label_text
43453                 ];
43454                 
43455                 if(this.indicatorpos == 'right') {
43456                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43457                     label.cn = [
43458                         label_text,
43459                         indicator
43460                     ];
43461                 }
43462                 
43463                 if(align == 'left') {
43464                     container = {
43465                         tag: 'div',
43466                         cn: [
43467                             container
43468                         ]
43469                     };
43470                     
43471                     if(this.labelWidth > 12){
43472                         label.style = "width: " + this.labelWidth + 'px';
43473                     }
43474                     if(this.labelWidth < 13 && this.labelmd == 0){
43475                         this.labelmd = this.labelWidth;
43476                     }
43477                     if(this.labellg > 0){
43478                         label.cls += ' col-lg-' + this.labellg;
43479                         input.cls += ' col-lg-' + (12 - this.labellg);
43480                     }
43481                     if(this.labelmd > 0){
43482                         label.cls += ' col-md-' + this.labelmd;
43483                         container.cls += ' col-md-' + (12 - this.labelmd);
43484                     }
43485                     if(this.labelsm > 0){
43486                         label.cls += ' col-sm-' + this.labelsm;
43487                         container.cls += ' col-sm-' + (12 - this.labelsm);
43488                     }
43489                     if(this.labelxs > 0){
43490                         label.cls += ' col-xs-' + this.labelxs;
43491                         container.cls += ' col-xs-' + (12 - this.labelxs);
43492                     }
43493                 }
43494             }
43495             
43496             cfg.cn = [
43497                 label,
43498                 container
43499             ];
43500             
43501             var settings = this;
43502             
43503             ['xs','sm','md','lg'].map(function(size){
43504                 if (settings[size]) {
43505                     cfg.cls += ' col-' + size + '-' + settings[size];
43506                 }
43507             });
43508             
43509             this.store = new Roo.data.Store({
43510                 proxy : new Roo.data.MemoryProxy({}),
43511                 reader : new Roo.data.JsonReader({
43512                     fields : [
43513                         {
43514                             'name' : 'name',
43515                             'type' : 'string'
43516                         },
43517                         {
43518                             'name' : 'iso2',
43519                             'type' : 'string'
43520                         },
43521                         {
43522                             'name' : 'dialCode',
43523                             'type' : 'string'
43524                         },
43525                         {
43526                             'name' : 'priority',
43527                             'type' : 'string'
43528                         },
43529                         {
43530                             'name' : 'areaCodes',
43531                             'type' : 'string'
43532                         }
43533                     ]
43534                 })
43535             });
43536             
43537             if(!this.preferedCountries) {
43538                 this.preferedCountries = [
43539                     'hk',
43540                     'gb',
43541                     'us'
43542                 ];
43543             }
43544             
43545             var p = this.preferedCountries.reverse();
43546             
43547             if(p) {
43548                 for (var i = 0; i < p.length; i++) {
43549                     for (var j = 0; j < this.allCountries.length; j++) {
43550                         if(this.allCountries[j].iso2 == p[i]) {
43551                             var t = this.allCountries[j];
43552                             this.allCountries.splice(j,1);
43553                             this.allCountries.unshift(t);
43554                         }
43555                     } 
43556                 }
43557             }
43558             
43559             this.store.proxy.data = {
43560                 success: true,
43561                 data: this.allCountries
43562             };
43563             
43564             return cfg;
43565         },
43566         
43567         initEvents : function()
43568         {
43569             this.createList();
43570             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43571             
43572             this.indicator = this.indicatorEl();
43573             this.flag = this.flagEl();
43574             this.dialCodeHolder = this.dialCodeHolderEl();
43575             
43576             this.trigger = this.el.select('div.flag-box',true).first();
43577             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43578             
43579             var _this = this;
43580             
43581             (function(){
43582                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43583                 _this.list.setWidth(lw);
43584             }).defer(100);
43585             
43586             this.list.on('mouseover', this.onViewOver, this);
43587             this.list.on('mousemove', this.onViewMove, this);
43588             this.inputEl().on("keyup", this.onKeyUp, this);
43589             this.inputEl().on("keypress", this.onKeyPress, this);
43590             
43591             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43592
43593             this.view = new Roo.View(this.list, this.tpl, {
43594                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43595             });
43596             
43597             this.view.on('click', this.onViewClick, this);
43598             this.setValue(this.defaultDialCode);
43599         },
43600         
43601         onTriggerClick : function(e)
43602         {
43603             Roo.log('trigger click');
43604             if(this.disabled){
43605                 return;
43606             }
43607             
43608             if(this.isExpanded()){
43609                 this.collapse();
43610                 this.hasFocus = false;
43611             }else {
43612                 this.store.load({});
43613                 this.hasFocus = true;
43614                 this.expand();
43615             }
43616         },
43617         
43618         isExpanded : function()
43619         {
43620             return this.list.isVisible();
43621         },
43622         
43623         collapse : function()
43624         {
43625             if(!this.isExpanded()){
43626                 return;
43627             }
43628             this.list.hide();
43629             Roo.get(document).un('mousedown', this.collapseIf, this);
43630             Roo.get(document).un('mousewheel', this.collapseIf, this);
43631             this.fireEvent('collapse', this);
43632             this.validate();
43633         },
43634         
43635         expand : function()
43636         {
43637             Roo.log('expand');
43638
43639             if(this.isExpanded() || !this.hasFocus){
43640                 return;
43641             }
43642             
43643             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43644             this.list.setWidth(lw);
43645             
43646             this.list.show();
43647             this.restrictHeight();
43648             
43649             Roo.get(document).on('mousedown', this.collapseIf, this);
43650             Roo.get(document).on('mousewheel', this.collapseIf, this);
43651             
43652             this.fireEvent('expand', this);
43653         },
43654         
43655         restrictHeight : function()
43656         {
43657             this.list.alignTo(this.inputEl(), this.listAlign);
43658             this.list.alignTo(this.inputEl(), this.listAlign);
43659         },
43660         
43661         onViewOver : function(e, t)
43662         {
43663             if(this.inKeyMode){
43664                 return;
43665             }
43666             var item = this.view.findItemFromChild(t);
43667             
43668             if(item){
43669                 var index = this.view.indexOf(item);
43670                 this.select(index, false);
43671             }
43672         },
43673
43674         // private
43675         onViewClick : function(view, doFocus, el, e)
43676         {
43677             var index = this.view.getSelectedIndexes()[0];
43678             
43679             var r = this.store.getAt(index);
43680             
43681             if(r){
43682                 this.onSelect(r, index);
43683             }
43684             if(doFocus !== false && !this.blockFocus){
43685                 this.inputEl().focus();
43686             }
43687         },
43688         
43689         onViewMove : function(e, t)
43690         {
43691             this.inKeyMode = false;
43692         },
43693         
43694         select : function(index, scrollIntoView)
43695         {
43696             this.selectedIndex = index;
43697             this.view.select(index);
43698             if(scrollIntoView !== false){
43699                 var el = this.view.getNode(index);
43700                 if(el){
43701                     this.list.scrollChildIntoView(el, false);
43702                 }
43703             }
43704         },
43705         
43706         createList : function()
43707         {
43708             this.list = Roo.get(document.body).createChild({
43709                 tag: 'ul',
43710                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43711                 style: 'display:none'
43712             });
43713             
43714             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43715         },
43716         
43717         collapseIf : function(e)
43718         {
43719             var in_combo  = e.within(this.el);
43720             var in_list =  e.within(this.list);
43721             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43722             
43723             if (in_combo || in_list || is_list) {
43724                 return;
43725             }
43726             this.collapse();
43727         },
43728         
43729         onSelect : function(record, index)
43730         {
43731             if(this.fireEvent('beforeselect', this, record, index) !== false){
43732                 
43733                 this.setFlagClass(record.data.iso2);
43734                 this.setDialCode(record.data.dialCode);
43735                 this.hasFocus = false;
43736                 this.collapse();
43737                 this.fireEvent('select', this, record, index);
43738             }
43739         },
43740         
43741         flagEl : function()
43742         {
43743             var flag = this.el.select('div.flag',true).first();
43744             if(!flag){
43745                 return false;
43746             }
43747             return flag;
43748         },
43749         
43750         dialCodeHolderEl : function()
43751         {
43752             var d = this.el.select('input.dial-code-holder',true).first();
43753             if(!d){
43754                 return false;
43755             }
43756             return d;
43757         },
43758         
43759         setDialCode : function(v)
43760         {
43761             this.dialCodeHolder.dom.value = '+'+v;
43762         },
43763         
43764         setFlagClass : function(n)
43765         {
43766             this.flag.dom.className = 'flag '+n;
43767         },
43768         
43769         getValue : function()
43770         {
43771             var v = this.inputEl().getValue();
43772             if(this.dialCodeHolder) {
43773                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43774             }
43775             return v;
43776         },
43777         
43778         setValue : function(v)
43779         {
43780             var d = this.getDialCode(v);
43781             
43782             //invalid dial code
43783             if(v.length == 0 || !d || d.length == 0) {
43784                 if(this.rendered){
43785                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43786                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43787                 }
43788                 return;
43789             }
43790             
43791             //valid dial code
43792             this.setFlagClass(this.dialCodeMapping[d].iso2);
43793             this.setDialCode(d);
43794             this.inputEl().dom.value = v.replace('+'+d,'');
43795             this.hiddenEl().dom.value = this.getValue();
43796             
43797             this.validate();
43798         },
43799         
43800         getDialCode : function(v)
43801         {
43802             v = v ||  '';
43803             
43804             if (v.length == 0) {
43805                 return this.dialCodeHolder.dom.value;
43806             }
43807             
43808             var dialCode = "";
43809             if (v.charAt(0) != "+") {
43810                 return false;
43811             }
43812             var numericChars = "";
43813             for (var i = 1; i < v.length; i++) {
43814               var c = v.charAt(i);
43815               if (!isNaN(c)) {
43816                 numericChars += c;
43817                 if (this.dialCodeMapping[numericChars]) {
43818                   dialCode = v.substr(1, i);
43819                 }
43820                 if (numericChars.length == 4) {
43821                   break;
43822                 }
43823               }
43824             }
43825             return dialCode;
43826         },
43827         
43828         reset : function()
43829         {
43830             this.setValue(this.defaultDialCode);
43831             this.validate();
43832         },
43833         
43834         hiddenEl : function()
43835         {
43836             return this.el.select('input.hidden-tel-input',true).first();
43837         },
43838         
43839         // after setting val
43840         onKeyUp : function(e){
43841             this.setValue(this.getValue());
43842         },
43843         
43844         onKeyPress : function(e){
43845             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43846                 e.stopEvent();
43847             }
43848         }
43849         
43850 });
43851 /**
43852  * @class Roo.bootstrap.MoneyField
43853  * @extends Roo.bootstrap.ComboBox
43854  * Bootstrap MoneyField class
43855  * 
43856  * @constructor
43857  * Create a new MoneyField.
43858  * @param {Object} config Configuration options
43859  */
43860
43861 Roo.bootstrap.MoneyField = function(config) {
43862     
43863     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43864     
43865 };
43866
43867 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43868     
43869     /**
43870      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43871      */
43872     allowDecimals : true,
43873     /**
43874      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43875      */
43876     decimalSeparator : ".",
43877     /**
43878      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43879      */
43880     decimalPrecision : 0,
43881     /**
43882      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43883      */
43884     allowNegative : true,
43885     /**
43886      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43887      */
43888     allowZero: true,
43889     /**
43890      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43891      */
43892     minValue : Number.NEGATIVE_INFINITY,
43893     /**
43894      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43895      */
43896     maxValue : Number.MAX_VALUE,
43897     /**
43898      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43899      */
43900     minText : "The minimum value for this field is {0}",
43901     /**
43902      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43903      */
43904     maxText : "The maximum value for this field is {0}",
43905     /**
43906      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43907      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43908      */
43909     nanText : "{0} is not a valid number",
43910     /**
43911      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43912      */
43913     castInt : true,
43914     /**
43915      * @cfg {String} defaults currency of the MoneyField
43916      * value should be in lkey
43917      */
43918     defaultCurrency : false,
43919     /**
43920      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43921      */
43922     thousandsDelimiter : false,
43923     /**
43924      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43925      */
43926     max_length: false,
43927     
43928     inputlg : 9,
43929     inputmd : 9,
43930     inputsm : 9,
43931     inputxs : 6,
43932      /**
43933      * @cfg {Roo.data.Store} store  Store to lookup currency??
43934      */
43935     store : false,
43936     
43937     getAutoCreate : function()
43938     {
43939         var align = this.labelAlign || this.parentLabelAlign();
43940         
43941         var id = Roo.id();
43942
43943         var cfg = {
43944             cls: 'form-group',
43945             cn: []
43946         };
43947
43948         var input =  {
43949             tag: 'input',
43950             id : id,
43951             cls : 'form-control roo-money-amount-input',
43952             autocomplete: 'new-password'
43953         };
43954         
43955         var hiddenInput = {
43956             tag: 'input',
43957             type: 'hidden',
43958             id: Roo.id(),
43959             cls: 'hidden-number-input'
43960         };
43961         
43962         if(this.max_length) {
43963             input.maxlength = this.max_length; 
43964         }
43965         
43966         if (this.name) {
43967             hiddenInput.name = this.name;
43968         }
43969
43970         if (this.disabled) {
43971             input.disabled = true;
43972         }
43973
43974         var clg = 12 - this.inputlg;
43975         var cmd = 12 - this.inputmd;
43976         var csm = 12 - this.inputsm;
43977         var cxs = 12 - this.inputxs;
43978         
43979         var container = {
43980             tag : 'div',
43981             cls : 'row roo-money-field',
43982             cn : [
43983                 {
43984                     tag : 'div',
43985                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43986                     cn : [
43987                         {
43988                             tag : 'div',
43989                             cls: 'roo-select2-container input-group',
43990                             cn: [
43991                                 {
43992                                     tag : 'input',
43993                                     cls : 'form-control roo-money-currency-input',
43994                                     autocomplete: 'new-password',
43995                                     readOnly : 1,
43996                                     name : this.currencyName
43997                                 },
43998                                 {
43999                                     tag :'span',
44000                                     cls : 'input-group-addon',
44001                                     cn : [
44002                                         {
44003                                             tag: 'span',
44004                                             cls: 'caret'
44005                                         }
44006                                     ]
44007                                 }
44008                             ]
44009                         }
44010                     ]
44011                 },
44012                 {
44013                     tag : 'div',
44014                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44015                     cn : [
44016                         {
44017                             tag: 'div',
44018                             cls: this.hasFeedback ? 'has-feedback' : '',
44019                             cn: [
44020                                 input
44021                             ]
44022                         }
44023                     ]
44024                 }
44025             ]
44026             
44027         };
44028         
44029         if (this.fieldLabel.length) {
44030             var indicator = {
44031                 tag: 'i',
44032                 tooltip: 'This field is required'
44033             };
44034
44035             var label = {
44036                 tag: 'label',
44037                 'for':  id,
44038                 cls: 'control-label',
44039                 cn: []
44040             };
44041
44042             var label_text = {
44043                 tag: 'span',
44044                 html: this.fieldLabel
44045             };
44046
44047             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44048             label.cn = [
44049                 indicator,
44050                 label_text
44051             ];
44052
44053             if(this.indicatorpos == 'right') {
44054                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44055                 label.cn = [
44056                     label_text,
44057                     indicator
44058                 ];
44059             }
44060
44061             if(align == 'left') {
44062                 container = {
44063                     tag: 'div',
44064                     cn: [
44065                         container
44066                     ]
44067                 };
44068
44069                 if(this.labelWidth > 12){
44070                     label.style = "width: " + this.labelWidth + 'px';
44071                 }
44072                 if(this.labelWidth < 13 && this.labelmd == 0){
44073                     this.labelmd = this.labelWidth;
44074                 }
44075                 if(this.labellg > 0){
44076                     label.cls += ' col-lg-' + this.labellg;
44077                     input.cls += ' col-lg-' + (12 - this.labellg);
44078                 }
44079                 if(this.labelmd > 0){
44080                     label.cls += ' col-md-' + this.labelmd;
44081                     container.cls += ' col-md-' + (12 - this.labelmd);
44082                 }
44083                 if(this.labelsm > 0){
44084                     label.cls += ' col-sm-' + this.labelsm;
44085                     container.cls += ' col-sm-' + (12 - this.labelsm);
44086                 }
44087                 if(this.labelxs > 0){
44088                     label.cls += ' col-xs-' + this.labelxs;
44089                     container.cls += ' col-xs-' + (12 - this.labelxs);
44090                 }
44091             }
44092         }
44093
44094         cfg.cn = [
44095             label,
44096             container,
44097             hiddenInput
44098         ];
44099         
44100         var settings = this;
44101
44102         ['xs','sm','md','lg'].map(function(size){
44103             if (settings[size]) {
44104                 cfg.cls += ' col-' + size + '-' + settings[size];
44105             }
44106         });
44107         
44108         return cfg;
44109     },
44110     
44111     initEvents : function()
44112     {
44113         this.indicator = this.indicatorEl();
44114         
44115         this.initCurrencyEvent();
44116         
44117         this.initNumberEvent();
44118     },
44119     
44120     initCurrencyEvent : function()
44121     {
44122         if (!this.store) {
44123             throw "can not find store for combo";
44124         }
44125         
44126         this.store = Roo.factory(this.store, Roo.data);
44127         this.store.parent = this;
44128         
44129         this.createList();
44130         
44131         this.triggerEl = this.el.select('.input-group-addon', true).first();
44132         
44133         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44134         
44135         var _this = this;
44136         
44137         (function(){
44138             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44139             _this.list.setWidth(lw);
44140         }).defer(100);
44141         
44142         this.list.on('mouseover', this.onViewOver, this);
44143         this.list.on('mousemove', this.onViewMove, this);
44144         this.list.on('scroll', this.onViewScroll, this);
44145         
44146         if(!this.tpl){
44147             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44148         }
44149         
44150         this.view = new Roo.View(this.list, this.tpl, {
44151             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44152         });
44153         
44154         this.view.on('click', this.onViewClick, this);
44155         
44156         this.store.on('beforeload', this.onBeforeLoad, this);
44157         this.store.on('load', this.onLoad, this);
44158         this.store.on('loadexception', this.onLoadException, this);
44159         
44160         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44161             "up" : function(e){
44162                 this.inKeyMode = true;
44163                 this.selectPrev();
44164             },
44165
44166             "down" : function(e){
44167                 if(!this.isExpanded()){
44168                     this.onTriggerClick();
44169                 }else{
44170                     this.inKeyMode = true;
44171                     this.selectNext();
44172                 }
44173             },
44174
44175             "enter" : function(e){
44176                 this.collapse();
44177                 
44178                 if(this.fireEvent("specialkey", this, e)){
44179                     this.onViewClick(false);
44180                 }
44181                 
44182                 return true;
44183             },
44184
44185             "esc" : function(e){
44186                 this.collapse();
44187             },
44188
44189             "tab" : function(e){
44190                 this.collapse();
44191                 
44192                 if(this.fireEvent("specialkey", this, e)){
44193                     this.onViewClick(false);
44194                 }
44195                 
44196                 return true;
44197             },
44198
44199             scope : this,
44200
44201             doRelay : function(foo, bar, hname){
44202                 if(hname == 'down' || this.scope.isExpanded()){
44203                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44204                 }
44205                 return true;
44206             },
44207
44208             forceKeyDown: true
44209         });
44210         
44211         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44212         
44213     },
44214     
44215     initNumberEvent : function(e)
44216     {
44217         this.inputEl().on("keydown" , this.fireKey,  this);
44218         this.inputEl().on("focus", this.onFocus,  this);
44219         this.inputEl().on("blur", this.onBlur,  this);
44220         
44221         this.inputEl().relayEvent('keyup', this);
44222         
44223         if(this.indicator){
44224             this.indicator.addClass('invisible');
44225         }
44226  
44227         this.originalValue = this.getValue();
44228         
44229         if(this.validationEvent == 'keyup'){
44230             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44231             this.inputEl().on('keyup', this.filterValidation, this);
44232         }
44233         else if(this.validationEvent !== false){
44234             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44235         }
44236         
44237         if(this.selectOnFocus){
44238             this.on("focus", this.preFocus, this);
44239             
44240         }
44241         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44242             this.inputEl().on("keypress", this.filterKeys, this);
44243         } else {
44244             this.inputEl().relayEvent('keypress', this);
44245         }
44246         
44247         var allowed = "0123456789";
44248         
44249         if(this.allowDecimals){
44250             allowed += this.decimalSeparator;
44251         }
44252         
44253         if(this.allowNegative){
44254             allowed += "-";
44255         }
44256         
44257         if(this.thousandsDelimiter) {
44258             allowed += ",";
44259         }
44260         
44261         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44262         
44263         var keyPress = function(e){
44264             
44265             var k = e.getKey();
44266             
44267             var c = e.getCharCode();
44268             
44269             if(
44270                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44271                     allowed.indexOf(String.fromCharCode(c)) === -1
44272             ){
44273                 e.stopEvent();
44274                 return;
44275             }
44276             
44277             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44278                 return;
44279             }
44280             
44281             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44282                 e.stopEvent();
44283             }
44284         };
44285         
44286         this.inputEl().on("keypress", keyPress, this);
44287         
44288     },
44289     
44290     onTriggerClick : function(e)
44291     {   
44292         if(this.disabled){
44293             return;
44294         }
44295         
44296         this.page = 0;
44297         this.loadNext = false;
44298         
44299         if(this.isExpanded()){
44300             this.collapse();
44301             return;
44302         }
44303         
44304         this.hasFocus = true;
44305         
44306         if(this.triggerAction == 'all') {
44307             this.doQuery(this.allQuery, true);
44308             return;
44309         }
44310         
44311         this.doQuery(this.getRawValue());
44312     },
44313     
44314     getCurrency : function()
44315     {   
44316         var v = this.currencyEl().getValue();
44317         
44318         return v;
44319     },
44320     
44321     restrictHeight : function()
44322     {
44323         this.list.alignTo(this.currencyEl(), this.listAlign);
44324         this.list.alignTo(this.currencyEl(), this.listAlign);
44325     },
44326     
44327     onViewClick : function(view, doFocus, el, e)
44328     {
44329         var index = this.view.getSelectedIndexes()[0];
44330         
44331         var r = this.store.getAt(index);
44332         
44333         if(r){
44334             this.onSelect(r, index);
44335         }
44336     },
44337     
44338     onSelect : function(record, index){
44339         
44340         if(this.fireEvent('beforeselect', this, record, index) !== false){
44341         
44342             this.setFromCurrencyData(index > -1 ? record.data : false);
44343             
44344             this.collapse();
44345             
44346             this.fireEvent('select', this, record, index);
44347         }
44348     },
44349     
44350     setFromCurrencyData : function(o)
44351     {
44352         var currency = '';
44353         
44354         this.lastCurrency = o;
44355         
44356         if (this.currencyField) {
44357             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44358         } else {
44359             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44360         }
44361         
44362         this.lastSelectionText = currency;
44363         
44364         //setting default currency
44365         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44366             this.setCurrency(this.defaultCurrency);
44367             return;
44368         }
44369         
44370         this.setCurrency(currency);
44371     },
44372     
44373     setFromData : function(o)
44374     {
44375         var c = {};
44376         
44377         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44378         
44379         this.setFromCurrencyData(c);
44380         
44381         var value = '';
44382         
44383         if (this.name) {
44384             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44385         } else {
44386             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44387         }
44388         
44389         this.setValue(value);
44390         
44391     },
44392     
44393     setCurrency : function(v)
44394     {   
44395         this.currencyValue = v;
44396         
44397         if(this.rendered){
44398             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44399             this.validate();
44400         }
44401     },
44402     
44403     setValue : function(v)
44404     {
44405         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44406         
44407         this.value = v;
44408         
44409         if(this.rendered){
44410             
44411             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44412             
44413             this.inputEl().dom.value = (v == '') ? '' :
44414                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44415             
44416             if(!this.allowZero && v === '0') {
44417                 this.hiddenEl().dom.value = '';
44418                 this.inputEl().dom.value = '';
44419             }
44420             
44421             this.validate();
44422         }
44423     },
44424     
44425     getRawValue : function()
44426     {
44427         var v = this.inputEl().getValue();
44428         
44429         return v;
44430     },
44431     
44432     getValue : function()
44433     {
44434         return this.fixPrecision(this.parseValue(this.getRawValue()));
44435     },
44436     
44437     parseValue : function(value)
44438     {
44439         if(this.thousandsDelimiter) {
44440             value += "";
44441             r = new RegExp(",", "g");
44442             value = value.replace(r, "");
44443         }
44444         
44445         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44446         return isNaN(value) ? '' : value;
44447         
44448     },
44449     
44450     fixPrecision : function(value)
44451     {
44452         if(this.thousandsDelimiter) {
44453             value += "";
44454             r = new RegExp(",", "g");
44455             value = value.replace(r, "");
44456         }
44457         
44458         var nan = isNaN(value);
44459         
44460         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44461             return nan ? '' : value;
44462         }
44463         return parseFloat(value).toFixed(this.decimalPrecision);
44464     },
44465     
44466     decimalPrecisionFcn : function(v)
44467     {
44468         return Math.floor(v);
44469     },
44470     
44471     validateValue : function(value)
44472     {
44473         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44474             return false;
44475         }
44476         
44477         var num = this.parseValue(value);
44478         
44479         if(isNaN(num)){
44480             this.markInvalid(String.format(this.nanText, value));
44481             return false;
44482         }
44483         
44484         if(num < this.minValue){
44485             this.markInvalid(String.format(this.minText, this.minValue));
44486             return false;
44487         }
44488         
44489         if(num > this.maxValue){
44490             this.markInvalid(String.format(this.maxText, this.maxValue));
44491             return false;
44492         }
44493         
44494         return true;
44495     },
44496     
44497     validate : function()
44498     {
44499         if(this.disabled || this.allowBlank){
44500             this.markValid();
44501             return true;
44502         }
44503         
44504         var currency = this.getCurrency();
44505         
44506         if(this.validateValue(this.getRawValue()) && currency.length){
44507             this.markValid();
44508             return true;
44509         }
44510         
44511         this.markInvalid();
44512         return false;
44513     },
44514     
44515     getName: function()
44516     {
44517         return this.name;
44518     },
44519     
44520     beforeBlur : function()
44521     {
44522         if(!this.castInt){
44523             return;
44524         }
44525         
44526         var v = this.parseValue(this.getRawValue());
44527         
44528         if(v || v == 0){
44529             this.setValue(v);
44530         }
44531     },
44532     
44533     onBlur : function()
44534     {
44535         this.beforeBlur();
44536         
44537         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44538             //this.el.removeClass(this.focusClass);
44539         }
44540         
44541         this.hasFocus = false;
44542         
44543         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44544             this.validate();
44545         }
44546         
44547         var v = this.getValue();
44548         
44549         if(String(v) !== String(this.startValue)){
44550             this.fireEvent('change', this, v, this.startValue);
44551         }
44552         
44553         this.fireEvent("blur", this);
44554     },
44555     
44556     inputEl : function()
44557     {
44558         return this.el.select('.roo-money-amount-input', true).first();
44559     },
44560     
44561     currencyEl : function()
44562     {
44563         return this.el.select('.roo-money-currency-input', true).first();
44564     },
44565     
44566     hiddenEl : function()
44567     {
44568         return this.el.select('input.hidden-number-input',true).first();
44569     }
44570     
44571 });/**
44572  * @class Roo.bootstrap.BezierSignature
44573  * @extends Roo.bootstrap.Component
44574  * Bootstrap BezierSignature class
44575  * This script refer to:
44576  *    Title: Signature Pad
44577  *    Author: szimek
44578  *    Availability: https://github.com/szimek/signature_pad
44579  *
44580  * @constructor
44581  * Create a new BezierSignature
44582  * @param {Object} config The config object
44583  */
44584
44585 Roo.bootstrap.BezierSignature = function(config){
44586     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44587     this.addEvents({
44588         "resize" : true
44589     });
44590 };
44591
44592 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44593 {
44594      
44595     curve_data: [],
44596     
44597     is_empty: true,
44598     
44599     mouse_btn_down: true,
44600     
44601     /**
44602      * @cfg {int} canvas height
44603      */
44604     canvas_height: '200px',
44605     
44606     /**
44607      * @cfg {float|function} Radius of a single dot.
44608      */ 
44609     dot_size: false,
44610     
44611     /**
44612      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44613      */
44614     min_width: 0.5,
44615     
44616     /**
44617      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44618      */
44619     max_width: 2.5,
44620     
44621     /**
44622      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44623      */
44624     throttle: 16,
44625     
44626     /**
44627      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44628      */
44629     min_distance: 5,
44630     
44631     /**
44632      * @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.
44633      */
44634     bg_color: 'rgba(0, 0, 0, 0)',
44635     
44636     /**
44637      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44638      */
44639     dot_color: 'black',
44640     
44641     /**
44642      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44643      */ 
44644     velocity_filter_weight: 0.7,
44645     
44646     /**
44647      * @cfg {function} Callback when stroke begin. 
44648      */
44649     onBegin: false,
44650     
44651     /**
44652      * @cfg {function} Callback when stroke end.
44653      */
44654     onEnd: false,
44655     
44656     getAutoCreate : function()
44657     {
44658         var cls = 'roo-signature column';
44659         
44660         if(this.cls){
44661             cls += ' ' + this.cls;
44662         }
44663         
44664         var col_sizes = [
44665             'lg',
44666             'md',
44667             'sm',
44668             'xs'
44669         ];
44670         
44671         for(var i = 0; i < col_sizes.length; i++) {
44672             if(this[col_sizes[i]]) {
44673                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44674             }
44675         }
44676         
44677         var cfg = {
44678             tag: 'div',
44679             cls: cls,
44680             cn: [
44681                 {
44682                     tag: 'div',
44683                     cls: 'roo-signature-body',
44684                     cn: [
44685                         {
44686                             tag: 'canvas',
44687                             cls: 'roo-signature-body-canvas',
44688                             height: this.canvas_height,
44689                             width: this.canvas_width
44690                         }
44691                     ]
44692                 },
44693                 {
44694                     tag: 'input',
44695                     type: 'file',
44696                     style: 'display: none'
44697                 }
44698             ]
44699         };
44700         
44701         return cfg;
44702     },
44703     
44704     initEvents: function() 
44705     {
44706         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44707         
44708         var canvas = this.canvasEl();
44709         
44710         // mouse && touch event swapping...
44711         canvas.dom.style.touchAction = 'none';
44712         canvas.dom.style.msTouchAction = 'none';
44713         
44714         this.mouse_btn_down = false;
44715         canvas.on('mousedown', this._handleMouseDown, this);
44716         canvas.on('mousemove', this._handleMouseMove, this);
44717         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44718         
44719         if (window.PointerEvent) {
44720             canvas.on('pointerdown', this._handleMouseDown, this);
44721             canvas.on('pointermove', this._handleMouseMove, this);
44722             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44723         }
44724         
44725         if ('ontouchstart' in window) {
44726             canvas.on('touchstart', this._handleTouchStart, this);
44727             canvas.on('touchmove', this._handleTouchMove, this);
44728             canvas.on('touchend', this._handleTouchEnd, this);
44729         }
44730         
44731         Roo.EventManager.onWindowResize(this.resize, this, true);
44732         
44733         // file input event
44734         this.fileEl().on('change', this.uploadImage, this);
44735         
44736         this.clear();
44737         
44738         this.resize();
44739     },
44740     
44741     resize: function(){
44742         
44743         var canvas = this.canvasEl().dom;
44744         var ctx = this.canvasElCtx();
44745         var img_data = false;
44746         
44747         if(canvas.width > 0) {
44748             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44749         }
44750         // setting canvas width will clean img data
44751         canvas.width = 0;
44752         
44753         var style = window.getComputedStyle ? 
44754             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44755             
44756         var padding_left = parseInt(style.paddingLeft) || 0;
44757         var padding_right = parseInt(style.paddingRight) || 0;
44758         
44759         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44760         
44761         if(img_data) {
44762             ctx.putImageData(img_data, 0, 0);
44763         }
44764     },
44765     
44766     _handleMouseDown: function(e)
44767     {
44768         if (e.browserEvent.which === 1) {
44769             this.mouse_btn_down = true;
44770             this.strokeBegin(e);
44771         }
44772     },
44773     
44774     _handleMouseMove: function (e)
44775     {
44776         if (this.mouse_btn_down) {
44777             this.strokeMoveUpdate(e);
44778         }
44779     },
44780     
44781     _handleMouseUp: function (e)
44782     {
44783         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44784             this.mouse_btn_down = false;
44785             this.strokeEnd(e);
44786         }
44787     },
44788     
44789     _handleTouchStart: function (e) {
44790         
44791         e.preventDefault();
44792         if (e.browserEvent.targetTouches.length === 1) {
44793             // var touch = e.browserEvent.changedTouches[0];
44794             // this.strokeBegin(touch);
44795             
44796              this.strokeBegin(e); // assume e catching the correct xy...
44797         }
44798     },
44799     
44800     _handleTouchMove: function (e) {
44801         e.preventDefault();
44802         // var touch = event.targetTouches[0];
44803         // _this._strokeMoveUpdate(touch);
44804         this.strokeMoveUpdate(e);
44805     },
44806     
44807     _handleTouchEnd: function (e) {
44808         var wasCanvasTouched = e.target === this.canvasEl().dom;
44809         if (wasCanvasTouched) {
44810             e.preventDefault();
44811             // var touch = event.changedTouches[0];
44812             // _this._strokeEnd(touch);
44813             this.strokeEnd(e);
44814         }
44815     },
44816     
44817     reset: function () {
44818         this._lastPoints = [];
44819         this._lastVelocity = 0;
44820         this._lastWidth = (this.min_width + this.max_width) / 2;
44821         this.canvasElCtx().fillStyle = this.dot_color;
44822     },
44823     
44824     strokeMoveUpdate: function(e)
44825     {
44826         this.strokeUpdate(e);
44827         
44828         if (this.throttle) {
44829             this.throttleStroke(this.strokeUpdate, this.throttle);
44830         }
44831         else {
44832             this.strokeUpdate(e);
44833         }
44834     },
44835     
44836     strokeBegin: function(e)
44837     {
44838         var newPointGroup = {
44839             color: this.dot_color,
44840             points: []
44841         };
44842         
44843         if (typeof this.onBegin === 'function') {
44844             this.onBegin(e);
44845         }
44846         
44847         this.curve_data.push(newPointGroup);
44848         this.reset();
44849         this.strokeUpdate(e);
44850     },
44851     
44852     strokeUpdate: function(e)
44853     {
44854         var rect = this.canvasEl().dom.getBoundingClientRect();
44855         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44856         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44857         var lastPoints = lastPointGroup.points;
44858         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44859         var isLastPointTooClose = lastPoint
44860             ? point.distanceTo(lastPoint) <= this.min_distance
44861             : false;
44862         var color = lastPointGroup.color;
44863         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44864             var curve = this.addPoint(point);
44865             if (!lastPoint) {
44866                 this.drawDot({color: color, point: point});
44867             }
44868             else if (curve) {
44869                 this.drawCurve({color: color, curve: curve});
44870             }
44871             lastPoints.push({
44872                 time: point.time,
44873                 x: point.x,
44874                 y: point.y
44875             });
44876         }
44877     },
44878     
44879     strokeEnd: function(e)
44880     {
44881         this.strokeUpdate(e);
44882         if (typeof this.onEnd === 'function') {
44883             this.onEnd(e);
44884         }
44885     },
44886     
44887     addPoint:  function (point) {
44888         var _lastPoints = this._lastPoints;
44889         _lastPoints.push(point);
44890         if (_lastPoints.length > 2) {
44891             if (_lastPoints.length === 3) {
44892                 _lastPoints.unshift(_lastPoints[0]);
44893             }
44894             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44895             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44896             _lastPoints.shift();
44897             return curve;
44898         }
44899         return null;
44900     },
44901     
44902     calculateCurveWidths: function (startPoint, endPoint) {
44903         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44904             (1 - this.velocity_filter_weight) * this._lastVelocity;
44905
44906         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44907         var widths = {
44908             end: newWidth,
44909             start: this._lastWidth
44910         };
44911         
44912         this._lastVelocity = velocity;
44913         this._lastWidth = newWidth;
44914         return widths;
44915     },
44916     
44917     drawDot: function (_a) {
44918         var color = _a.color, point = _a.point;
44919         var ctx = this.canvasElCtx();
44920         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44921         ctx.beginPath();
44922         this.drawCurveSegment(point.x, point.y, width);
44923         ctx.closePath();
44924         ctx.fillStyle = color;
44925         ctx.fill();
44926     },
44927     
44928     drawCurve: function (_a) {
44929         var color = _a.color, curve = _a.curve;
44930         var ctx = this.canvasElCtx();
44931         var widthDelta = curve.endWidth - curve.startWidth;
44932         var drawSteps = Math.floor(curve.length()) * 2;
44933         ctx.beginPath();
44934         ctx.fillStyle = color;
44935         for (var i = 0; i < drawSteps; i += 1) {
44936         var t = i / drawSteps;
44937         var tt = t * t;
44938         var ttt = tt * t;
44939         var u = 1 - t;
44940         var uu = u * u;
44941         var uuu = uu * u;
44942         var x = uuu * curve.startPoint.x;
44943         x += 3 * uu * t * curve.control1.x;
44944         x += 3 * u * tt * curve.control2.x;
44945         x += ttt * curve.endPoint.x;
44946         var y = uuu * curve.startPoint.y;
44947         y += 3 * uu * t * curve.control1.y;
44948         y += 3 * u * tt * curve.control2.y;
44949         y += ttt * curve.endPoint.y;
44950         var width = curve.startWidth + ttt * widthDelta;
44951         this.drawCurveSegment(x, y, width);
44952         }
44953         ctx.closePath();
44954         ctx.fill();
44955     },
44956     
44957     drawCurveSegment: function (x, y, width) {
44958         var ctx = this.canvasElCtx();
44959         ctx.moveTo(x, y);
44960         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44961         this.is_empty = false;
44962     },
44963     
44964     clear: function()
44965     {
44966         var ctx = this.canvasElCtx();
44967         var canvas = this.canvasEl().dom;
44968         ctx.fillStyle = this.bg_color;
44969         ctx.clearRect(0, 0, canvas.width, canvas.height);
44970         ctx.fillRect(0, 0, canvas.width, canvas.height);
44971         this.curve_data = [];
44972         this.reset();
44973         this.is_empty = true;
44974     },
44975     
44976     fileEl: function()
44977     {
44978         return  this.el.select('input',true).first();
44979     },
44980     
44981     canvasEl: function()
44982     {
44983         return this.el.select('canvas',true).first();
44984     },
44985     
44986     canvasElCtx: function()
44987     {
44988         return this.el.select('canvas',true).first().dom.getContext('2d');
44989     },
44990     
44991     getImage: function(type)
44992     {
44993         if(this.is_empty) {
44994             return false;
44995         }
44996         
44997         // encryption ?
44998         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44999     },
45000     
45001     drawFromImage: function(img_src)
45002     {
45003         var img = new Image();
45004         
45005         img.onload = function(){
45006             this.canvasElCtx().drawImage(img, 0, 0);
45007         }.bind(this);
45008         
45009         img.src = img_src;
45010         
45011         this.is_empty = false;
45012     },
45013     
45014     selectImage: function()
45015     {
45016         this.fileEl().dom.click();
45017     },
45018     
45019     uploadImage: function(e)
45020     {
45021         var reader = new FileReader();
45022         
45023         reader.onload = function(e){
45024             var img = new Image();
45025             img.onload = function(){
45026                 this.reset();
45027                 this.canvasElCtx().drawImage(img, 0, 0);
45028             }.bind(this);
45029             img.src = e.target.result;
45030         }.bind(this);
45031         
45032         reader.readAsDataURL(e.target.files[0]);
45033     },
45034     
45035     // Bezier Point Constructor
45036     Point: (function () {
45037         function Point(x, y, time) {
45038             this.x = x;
45039             this.y = y;
45040             this.time = time || Date.now();
45041         }
45042         Point.prototype.distanceTo = function (start) {
45043             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45044         };
45045         Point.prototype.equals = function (other) {
45046             return this.x === other.x && this.y === other.y && this.time === other.time;
45047         };
45048         Point.prototype.velocityFrom = function (start) {
45049             return this.time !== start.time
45050             ? this.distanceTo(start) / (this.time - start.time)
45051             : 0;
45052         };
45053         return Point;
45054     }()),
45055     
45056     
45057     // Bezier Constructor
45058     Bezier: (function () {
45059         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45060             this.startPoint = startPoint;
45061             this.control2 = control2;
45062             this.control1 = control1;
45063             this.endPoint = endPoint;
45064             this.startWidth = startWidth;
45065             this.endWidth = endWidth;
45066         }
45067         Bezier.fromPoints = function (points, widths, scope) {
45068             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45069             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45070             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45071         };
45072         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45073             var dx1 = s1.x - s2.x;
45074             var dy1 = s1.y - s2.y;
45075             var dx2 = s2.x - s3.x;
45076             var dy2 = s2.y - s3.y;
45077             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45078             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45079             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45080             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45081             var dxm = m1.x - m2.x;
45082             var dym = m1.y - m2.y;
45083             var k = l2 / (l1 + l2);
45084             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45085             var tx = s2.x - cm.x;
45086             var ty = s2.y - cm.y;
45087             return {
45088                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45089                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45090             };
45091         };
45092         Bezier.prototype.length = function () {
45093             var steps = 10;
45094             var length = 0;
45095             var px;
45096             var py;
45097             for (var i = 0; i <= steps; i += 1) {
45098                 var t = i / steps;
45099                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45100                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45101                 if (i > 0) {
45102                     var xdiff = cx - px;
45103                     var ydiff = cy - py;
45104                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45105                 }
45106                 px = cx;
45107                 py = cy;
45108             }
45109             return length;
45110         };
45111         Bezier.prototype.point = function (t, start, c1, c2, end) {
45112             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45113             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45114             + (3.0 * c2 * (1.0 - t) * t * t)
45115             + (end * t * t * t);
45116         };
45117         return Bezier;
45118     }()),
45119     
45120     throttleStroke: function(fn, wait) {
45121       if (wait === void 0) { wait = 250; }
45122       var previous = 0;
45123       var timeout = null;
45124       var result;
45125       var storedContext;
45126       var storedArgs;
45127       var later = function () {
45128           previous = Date.now();
45129           timeout = null;
45130           result = fn.apply(storedContext, storedArgs);
45131           if (!timeout) {
45132               storedContext = null;
45133               storedArgs = [];
45134           }
45135       };
45136       return function wrapper() {
45137           var args = [];
45138           for (var _i = 0; _i < arguments.length; _i++) {
45139               args[_i] = arguments[_i];
45140           }
45141           var now = Date.now();
45142           var remaining = wait - (now - previous);
45143           storedContext = this;
45144           storedArgs = args;
45145           if (remaining <= 0 || remaining > wait) {
45146               if (timeout) {
45147                   clearTimeout(timeout);
45148                   timeout = null;
45149               }
45150               previous = now;
45151               result = fn.apply(storedContext, storedArgs);
45152               if (!timeout) {
45153                   storedContext = null;
45154                   storedArgs = [];
45155               }
45156           }
45157           else if (!timeout) {
45158               timeout = window.setTimeout(later, remaining);
45159           }
45160           return result;
45161       };
45162   }
45163   
45164 });
45165
45166  
45167
45168